From 32d5a363f869fd8661e3451cbdbfbdc373f1ab68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 26 Jun 2025 14:28:57 +0200 Subject: [PATCH 01/13] Move HotReload to a package --- AspNetCore.slnx | 5 +++- eng/Baseline.xml | 1 + eng/ProjectReferences.props | 1 + ...rosoft.DotNet.HotReload.WebAssembly.csproj | 30 +++++++++++++++++++ .../HotReload/src/PublicAPI.Shipped.txt | 0 .../HotReload/src/PublicAPI.Unshipped.txt | 23 ++++++++++++++ .../src}/WebAssemblyHotReload.cs | 30 ++++++++++++++----- ...DotNet.HotReload.WebAssembly.lib.module.js | 4 +++ .../src/Hosting/WebAssemblyHost.cs | 11 ++++--- ...t.AspNetCore.Components.WebAssembly.csproj | 7 +++-- .../WebAssembly/src/PublicAPI.Shipped.txt | 3 -- .../WebAssembly/src/PublicAPI.Unshipped.txt | 20 ------------- 12 files changed, 96 insertions(+), 39 deletions(-) create mode 100644 src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.csproj create mode 100644 src/Components/WebAssembly/HotReload/src/PublicAPI.Shipped.txt create mode 100644 src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt rename src/Components/WebAssembly/{WebAssembly/src/HotReload => HotReload/src}/WebAssemblyHotReload.cs (88%) create mode 100644 src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js diff --git a/AspNetCore.slnx b/AspNetCore.slnx index b73adbbb4b85..576eb51908ad 100644 --- a/AspNetCore.slnx +++ b/AspNetCore.slnx @@ -109,6 +109,9 @@ + + + @@ -1198,8 +1201,8 @@ - + diff --git a/eng/Baseline.xml b/eng/Baseline.xml index 1b4ce55e8e33..64f7c64e94fc 100644 --- a/eng/Baseline.xml +++ b/eng/Baseline.xml @@ -42,6 +42,7 @@ Update this list when preparing for a new patch. + diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 4dcee4af652b..a689a7bb2cbb 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -154,6 +154,7 @@ + diff --git a/src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.csproj b/src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.csproj new file mode 100644 index 000000000000..f78226e5191a --- /dev/null +++ b/src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.csproj @@ -0,0 +1,30 @@ + + + + $(DefaultNetCoreTargetFramework) + HotReload package for WebAssembly + $(NoWarn);BL0006 + + $(NoWarn);CS8603 + true + enable + true + true + + false + + + + + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/HotReload/src/PublicAPI.Shipped.txt b/src/Components/WebAssembly/HotReload/src/PublicAPI.Shipped.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt b/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt new file mode 100644 index 000000000000..9d38efd36c49 --- /dev/null +++ b/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt @@ -0,0 +1,23 @@ +#nullable enable +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload +static Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.ApplyHotReloadDelta(string! moduleIdString, byte[]! metadataDelta, byte[]! ilDelta, byte[]! pdbBytes, int[]? updatedTypes) -> void +static Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.GetApplyUpdateCapabilities() -> string! +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.Delta() -> void +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.ILDelta.get -> byte[]! +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.ILDelta.init -> void +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.MetadataDelta.get -> byte[]! +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.MetadataDelta.init -> void +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.ModuleId.get -> string! +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.ModuleId.init -> void +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.PdbDelta.get -> byte[]! +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.PdbDelta.init -> void +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.UpdatedTypes.get -> int[]! +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.UpdatedTypes.init -> void +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.LogEntry() -> void +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.Message.get -> string! +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.Message.init -> void +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.Severity.get -> int +Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.Severity.init -> void +static Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.ApplyHotReloadDeltas(Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta[]! deltas, int loggingLevel) -> Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry[]! diff --git a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs similarity index 88% rename from src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs rename to src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs index 907e3042aa7f..5d363c4e90c8 100644 --- a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs +++ b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs @@ -6,14 +6,15 @@ using System.Globalization; using System.Linq; using System.Net.Http; +using System.Reflection.Metadata; +using System.Runtime.InteropServices.JavaScript; +using System.Runtime.Versioning; using System.Text.Json; -using Microsoft.AspNetCore.Components.WebAssembly.Services; -using Microsoft.DotNet.HotReload; using Microsoft.JSInterop; #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member -namespace Microsoft.AspNetCore.Components.WebAssembly.HotReload; +namespace Microsoft.DotNet.HotReload.WebAssembly; /// /// Contains methods called by interop. Intended for framework use only, not supported for use in application @@ -62,7 +63,7 @@ public readonly struct Delta private static bool s_initialized; private static HotReloadAgent? s_hotReloadAgent; - internal static async Task InitializeAsync() + internal static async Task InitializeAsync(string baseUri) { if (Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") == "true" && OperatingSystem.IsBrowser()) @@ -77,17 +78,17 @@ internal static async Task InitializeAsync() throw new InvalidOperationException("Hot Reload agent already initialized"); } - await ApplyPreviousDeltasAsync(agent); + await ApplyPreviousDeltasAsync(agent, baseUri); } } - private static async ValueTask ApplyPreviousDeltasAsync(HotReloadAgent agent) + private static async ValueTask ApplyPreviousDeltasAsync(HotReloadAgent agent, string baseUri) { string errorMessage; using var client = new HttpClient() { - BaseAddress = new Uri(WebAssemblyNavigationManager.Instance.BaseUri, UriKind.Absolute) + BaseAddress = new Uri(baseUri, UriKind.Absolute) }; try @@ -163,3 +164,18 @@ public static LogEntry[] ApplyHotReloadDeltas(Delta[] deltas, int loggingLevel) public static string GetApplyUpdateCapabilities() => GetAgent()?.Capabilities ?? ""; } + +internal static partial class Interop +{ + [JSExport] + [SupportedOSPlatform("browser")] + public static Task InitializeAsync(string baseUri) + { + if (MetadataUpdater.IsSupported) + { + return WebAssemblyHotReload.InitializeAsync(baseUri); + } + + return Task.CompletedTask; + } +} diff --git a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js new file mode 100644 index 000000000000..3e085096502a --- /dev/null +++ b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js @@ -0,0 +1,4 @@ +export async function onRuntimeReady({ getAssemblyExports }) { + const exports = await getAssemblyExports("Microsoft.DotNet.HotReload.WebAssembly"); + await exports.Microsoft.AspNetCore.Components.WebAssembly.HotReload.Interop.InitializeAsync(document.baseURI); +} diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs index 21607a9f29d9..cd89dc625b72 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs @@ -2,11 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; -using System.Reflection.Metadata; using Microsoft.AspNetCore.Components.Infrastructure; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web.Infrastructure; -using Microsoft.AspNetCore.Components.WebAssembly.HotReload; using Microsoft.AspNetCore.Components.WebAssembly.Infrastructure; using Microsoft.AspNetCore.Components.WebAssembly.Rendering; using Microsoft.AspNetCore.Components.WebAssembly.Services; @@ -139,10 +137,11 @@ internal async Task RunAsyncCore(CancellationToken cancellationToken, WebAssembl manager.SetPlatformRenderMode(RenderMode.InteractiveWebAssembly); await manager.RestoreStateAsync(store); - if (MetadataUpdater.IsSupported) - { - await WebAssemblyHotReload.InitializeAsync(); - } + // TODO MF: Clean up + //if (MetadataUpdater.IsSupported) + //{ + // await WebAssemblyHotReload.InitializeAsync(); + //} var tcs = new TaskCompletionSource(); using (cancellationToken.Register(() => tcs.TrySetResult())) diff --git a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index 957925b03276..d8152894e88b 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -21,8 +21,7 @@ - - + @@ -67,4 +66,8 @@ + + + + diff --git a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt index 61755ac3eb86..4a00bf96a2a1 100644 --- a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt +++ b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt @@ -80,7 +80,6 @@ Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration.this[string! key].set -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration.WebAssemblyHostConfiguration() -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache.Default = 0 -> Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache.ForceCache = 4 -> Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache @@ -107,8 +106,6 @@ static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnviro static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsEnvironment(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment, string! environmentName) -> bool static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsProduction(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment) -> bool static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsStaging(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment) -> bool -static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.ApplyHotReloadDelta(string! moduleIdString, byte[]! metadataDelta, byte[]! ilDelta, byte[]! pdbBytes, int[]? updatedTypes) -> void -static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.GetApplyUpdateCapabilities() -> string! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestCache(this System.Net.Http.HttpRequestMessage! requestMessage, Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache requestCache) -> System.Net.Http.HttpRequestMessage! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestCredentials(this System.Net.Http.HttpRequestMessage! requestMessage, Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCredentials requestCredentials) -> System.Net.Http.HttpRequestMessage! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestIntegrity(this System.Net.Http.HttpRequestMessage! requestMessage, string! integrity) -> System.Net.Http.HttpRequestMessage! diff --git a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt index f9836324fe1f..e69de29bb2d1 100644 --- a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt +++ b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt @@ -1,20 +0,0 @@ -#nullable enable -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.Delta() -> void -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ILDelta.get -> byte[]! -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ILDelta.init -> void -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.MetadataDelta.get -> byte[]! -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.MetadataDelta.init -> void -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ModuleId.get -> string! -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ModuleId.init -> void -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.PdbDelta.get -> byte[]! -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.PdbDelta.init -> void -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.UpdatedTypes.get -> int[]! -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.UpdatedTypes.init -> void -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.LogEntry() -> void -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Message.get -> string! -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Message.init -> void -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Severity.get -> int -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Severity.init -> void -static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.ApplyHotReloadDeltas(Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta[]! deltas, int loggingLevel) -> Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry[]! From 43ca083c8c03ec69d02715e085907f61fe04da32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 26 Jun 2025 14:43:42 +0200 Subject: [PATCH 02/13] Fix --- .../Microsoft.DotNet.HotReload.WebAssembly.lib.module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js index 3e085096502a..3b7387040838 100644 --- a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js +++ b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js @@ -1,4 +1,4 @@ export async function onRuntimeReady({ getAssemblyExports }) { const exports = await getAssemblyExports("Microsoft.DotNet.HotReload.WebAssembly"); - await exports.Microsoft.AspNetCore.Components.WebAssembly.HotReload.Interop.InitializeAsync(document.baseURI); + await exports.Microsoft.DotNet.HotReload.WebAssembly.Interop.InitializeAsync(document.baseURI); } From d239c8a86feb9c11d2b91f4decd81f214e43fff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Mon, 14 Jul 2025 12:44:51 +0200 Subject: [PATCH 03/13] Add to slnf --- src/Components/Components.slnf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Components/Components.slnf b/src/Components/Components.slnf index aa83a0f8a9ef..6a47b56b2929 100644 --- a/src/Components/Components.slnf +++ b/src/Components/Components.slnf @@ -37,6 +37,7 @@ "src\\Components\\WebAssembly\\WebAssembly.Authentication\\test\\Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj", "src\\Components\\WebAssembly\\WebAssembly\\src\\Microsoft.AspNetCore.Components.WebAssembly.csproj", "src\\Components\\WebAssembly\\WebAssembly\\test\\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj", + "src\\Components\\WebAssembly\\HotReload\\src\\Microsoft.DotNet.HotReload.WebAssembly.csproj", "src\\Components\\WebAssembly\\testassets\\CustomBasePathApp\\CustomBasePathApp.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj", From 729b8cf561dba86abb4adffe4cdce23137f851d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Mon, 14 Jul 2025 12:45:08 +0200 Subject: [PATCH 04/13] Replace assembly in JS --- src/Components/Web.JS/src/Boot.WebAssembly.Common.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts b/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts index a5bb02d5b80d..d5bea0fcdaed 100644 --- a/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts +++ b/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts @@ -100,14 +100,14 @@ async function startCore(components: RootComponentManager { - dispatcher.invokeDotNetStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'ApplyHotReloadDelta', id, metadataDelta, ilDelta, pdbDelta, updatedTypes ?? null); + dispatcher.invokeDotNetStaticMethod('Microsoft.DotNet.HotReload.WebAssembly', 'ApplyHotReloadDelta', id, metadataDelta, ilDelta, pdbDelta, updatedTypes ?? null); }; Blazor._internal.applyHotReloadDeltas = (deltas: { moduleId: string, metadataDelta: string, ilDelta: string, pdbDelta: string, updatedTypes: number[] }[], loggingLevel: number) => { - return dispatcher.invokeDotNetStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'ApplyHotReloadDeltas', deltas, loggingLevel) ?? []; + return dispatcher.invokeDotNetStaticMethod('Microsoft.DotNet.HotReload.WebAssembly', 'ApplyHotReloadDeltas', deltas, loggingLevel) ?? []; }; - Blazor._internal.getApplyUpdateCapabilities = () => dispatcher.invokeDotNetStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'GetApplyUpdateCapabilities') ?? ''; + Blazor._internal.getApplyUpdateCapabilities = () => dispatcher.invokeDotNetStaticMethod('Microsoft.DotNet.HotReload.WebAssembly', 'GetApplyUpdateCapabilities') ?? ''; // Configure JS interop Blazor._internal.invokeJSJson = invokeJSJson; @@ -134,7 +134,7 @@ async function startCore(components: RootComponentManager => { await dispatcher.invokeDotNetStaticMethodAsync( - 'Microsoft.AspNetCore.Components.WebAssembly', + 'Microsoft.DotNet.HotReload.WebAssembly', 'NotifyLocationChanged', uri, state, @@ -142,7 +142,7 @@ async function startCore(components: RootComponentManager => { const shouldContinueNavigation = await dispatcher.invokeDotNetStaticMethodAsync( - 'Microsoft.AspNetCore.Components.WebAssembly', + 'Microsoft.DotNet.HotReload.WebAssembly', 'NotifyLocationChangingAsync', uri, state, From a1df69ddfc41a3c38ac1b0d4d6e6c3a03f5868c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 15 Jul 2025 17:38:52 +0200 Subject: [PATCH 05/13] Switch between built-in and referenced hotreload --- .../Web.JS/src/Boot.WebAssembly.Common.ts | 10 +- .../HotReload/src/WebAssemblyHotReload.cs | 9 +- .../src/Hosting/WebAssemblyHost.cs | 11 +- .../src/HotReload/WebAssemblyHotReload.cs | 165 ++++++++++++++++++ ...t.AspNetCore.Components.WebAssembly.csproj | 6 +- 5 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs diff --git a/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts b/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts index d5bea0fcdaed..a5bb02d5b80d 100644 --- a/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts +++ b/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts @@ -100,14 +100,14 @@ async function startCore(components: RootComponentManager { - dispatcher.invokeDotNetStaticMethod('Microsoft.DotNet.HotReload.WebAssembly', 'ApplyHotReloadDelta', id, metadataDelta, ilDelta, pdbDelta, updatedTypes ?? null); + dispatcher.invokeDotNetStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'ApplyHotReloadDelta', id, metadataDelta, ilDelta, pdbDelta, updatedTypes ?? null); }; Blazor._internal.applyHotReloadDeltas = (deltas: { moduleId: string, metadataDelta: string, ilDelta: string, pdbDelta: string, updatedTypes: number[] }[], loggingLevel: number) => { - return dispatcher.invokeDotNetStaticMethod('Microsoft.DotNet.HotReload.WebAssembly', 'ApplyHotReloadDeltas', deltas, loggingLevel) ?? []; + return dispatcher.invokeDotNetStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'ApplyHotReloadDeltas', deltas, loggingLevel) ?? []; }; - Blazor._internal.getApplyUpdateCapabilities = () => dispatcher.invokeDotNetStaticMethod('Microsoft.DotNet.HotReload.WebAssembly', 'GetApplyUpdateCapabilities') ?? ''; + Blazor._internal.getApplyUpdateCapabilities = () => dispatcher.invokeDotNetStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'GetApplyUpdateCapabilities') ?? ''; // Configure JS interop Blazor._internal.invokeJSJson = invokeJSJson; @@ -134,7 +134,7 @@ async function startCore(components: RootComponentManager => { await dispatcher.invokeDotNetStaticMethodAsync( - 'Microsoft.DotNet.HotReload.WebAssembly', + 'Microsoft.AspNetCore.Components.WebAssembly', 'NotifyLocationChanged', uri, state, @@ -142,7 +142,7 @@ async function startCore(components: RootComponentManager => { const shouldContinueNavigation = await dispatcher.invokeDotNetStaticMethodAsync( - 'Microsoft.DotNet.HotReload.WebAssembly', + 'Microsoft.AspNetCore.Components.WebAssembly', 'NotifyLocationChangingAsync', uri, state, diff --git a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs index 5d363c4e90c8..cc2e735e1da8 100644 --- a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs +++ b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs @@ -135,7 +135,6 @@ private static async ValueTask ApplyPreviousDeltasAsync(HotReloadAgent agent, st /// For framework use only. /// [Obsolete("Use ApplyHotReloadDeltas instead")] - [JSInvokable(nameof(ApplyHotReloadDelta))] public static void ApplyHotReloadDelta(string moduleIdString, byte[] metadataDelta, byte[] ilDelta, byte[] pdbBytes, int[]? updatedTypes) { GetAgent()?.ApplyDeltas( @@ -160,7 +159,6 @@ public static LogEntry[] ApplyHotReloadDeltas(Delta[] deltas, int loggingLevel) /// /// For framework use only. /// - [JSInvokable(nameof(GetApplyUpdateCapabilities))] public static string GetApplyUpdateCapabilities() => GetAgent()?.Capabilities ?? ""; } @@ -178,4 +176,11 @@ public static Task InitializeAsync(string baseUri) return Task.CompletedTask; } + + [JSExport] + [SupportedOSPlatform("browser")] + public static string GetApplyUpdateCapabilities() + { + return WebAssemblyHotReload.GetApplyUpdateCapabilities(); + } } diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs index cd89dc625b72..bd4c88059729 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; using Microsoft.AspNetCore.Components.Infrastructure; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web.Infrastructure; +using Microsoft.AspNetCore.Components.WebAssembly.HotReload; using Microsoft.AspNetCore.Components.WebAssembly.Infrastructure; using Microsoft.AspNetCore.Components.WebAssembly.Rendering; using Microsoft.AspNetCore.Components.WebAssembly.Services; @@ -137,11 +139,10 @@ internal async Task RunAsyncCore(CancellationToken cancellationToken, WebAssembl manager.SetPlatformRenderMode(RenderMode.InteractiveWebAssembly); await manager.RestoreStateAsync(store); - // TODO MF: Clean up - //if (MetadataUpdater.IsSupported) - //{ - // await WebAssemblyHotReload.InitializeAsync(); - //} + if (MetadataUpdater.IsSupported && Environment.GetEnvironmentVariable("__BLAZOR_WEBASSEMBLY_LEGACY_HOTRELOAD") != "false") + { + await WebAssemblyHotReload.InitializeAsync(); + } var tcs = new TaskCompletionSource(); using (cancellationToken.Register(() => tcs.TrySetResult())) diff --git a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs b/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs new file mode 100644 index 000000000000..907e3042aa7f --- /dev/null +++ b/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs @@ -0,0 +1,165 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using Microsoft.AspNetCore.Components.WebAssembly.Services; +using Microsoft.DotNet.HotReload; +using Microsoft.JSInterop; + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +namespace Microsoft.AspNetCore.Components.WebAssembly.HotReload; + +/// +/// Contains methods called by interop. Intended for framework use only, not supported for use in application +/// code. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +[UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "Hot Reload does not support trimming")] +public static partial class WebAssemblyHotReload +{ + /// + /// For framework use only. + /// + public readonly struct LogEntry + { + public string Message { get; init; } + public int Severity { get; init; } + } + + /// + /// For framework use only. + /// + internal sealed class Update + { + public int Id { get; set; } + public Delta[] Deltas { get; set; } = default!; + } + + /// + /// For framework use only. + /// + public readonly struct Delta + { + public string ModuleId { get; init; } + public byte[] MetadataDelta { get; init; } + public byte[] ILDelta { get; init; } + public byte[] PdbDelta { get; init; } + public int[] UpdatedTypes { get; init; } + } + + private static readonly AgentReporter s_reporter = new(); + private static readonly JsonSerializerOptions s_jsonSerializerOptions = new(JsonSerializerDefaults.Web); + + private static bool s_initialized; + private static HotReloadAgent? s_hotReloadAgent; + + internal static async Task InitializeAsync() + { + if (Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") == "true" && + OperatingSystem.IsBrowser()) + { + s_initialized = true; + + var agent = new HotReloadAgent(); + + var existingAgent = Interlocked.CompareExchange(ref s_hotReloadAgent, agent, null); + if (existingAgent != null) + { + throw new InvalidOperationException("Hot Reload agent already initialized"); + } + + await ApplyPreviousDeltasAsync(agent); + } + } + + private static async ValueTask ApplyPreviousDeltasAsync(HotReloadAgent agent) + { + string errorMessage; + + using var client = new HttpClient() + { + BaseAddress = new Uri(WebAssemblyNavigationManager.Instance.BaseUri, UriKind.Absolute) + }; + + try + { + var response = await client.GetAsync("/_framework/blazor-hotreload"); + if (response.IsSuccessStatusCode) + { + var deltasJson = await response.Content.ReadAsStringAsync(); + var updates = deltasJson != "" ? JsonSerializer.Deserialize(deltasJson, s_jsonSerializerOptions) : null; + if (updates == null) + { + s_reporter.Report($"No previous updates to apply.", AgentMessageSeverity.Verbose); + return; + } + + var i = 1; + foreach (var update in updates) + { + s_reporter.Report($"Reapplying update {i}/{updates.Length}.", AgentMessageSeverity.Verbose); + + agent.ApplyDeltas( + update.Deltas.Select(d => new UpdateDelta(Guid.Parse(d.ModuleId, CultureInfo.InvariantCulture), d.MetadataDelta, d.ILDelta, d.PdbDelta, d.UpdatedTypes))); + + i++; + } + + return; + } + + errorMessage = $"HTTP GET '/_framework/blazor-hotreload' returned {response.StatusCode}"; + } + catch (Exception e) + { + errorMessage = e.ToString(); + } + + s_reporter.Report($"Failed to retrieve and apply previous deltas from the server: ${errorMessage}", AgentMessageSeverity.Error); + } + + private static HotReloadAgent? GetAgent() + => s_hotReloadAgent ?? (s_initialized ? throw new InvalidOperationException("Hot Reload agent not initialized") : null); + + /// + /// For framework use only. + /// + [Obsolete("Use ApplyHotReloadDeltas instead")] + [JSInvokable(nameof(ApplyHotReloadDelta))] + public static void ApplyHotReloadDelta(string moduleIdString, byte[] metadataDelta, byte[] ilDelta, byte[] pdbBytes, int[]? updatedTypes) + { + GetAgent()?.ApplyDeltas( + [new UpdateDelta(Guid.Parse(moduleIdString, CultureInfo.InvariantCulture), metadataDelta, ilDelta, pdbBytes, updatedTypes ?? [])]); + } + + /// + /// For framework use only. + /// + [JSInvokable(nameof(ApplyHotReloadDeltas))] + public static LogEntry[] ApplyHotReloadDeltas(Delta[] deltas, int loggingLevel) + { + var agent = GetAgent(); + + agent?.ApplyDeltas( + deltas.Select(d => new UpdateDelta(Guid.Parse(d.ModuleId, CultureInfo.InvariantCulture), d.MetadataDelta, d.ILDelta, d.PdbDelta, d.UpdatedTypes))); + + return s_reporter.GetAndClearLogEntries((ResponseLoggingLevel)loggingLevel) + .Select(log => new LogEntry() { Message = log.message, Severity = (int)log.severity }).ToArray(); + } + + /// + /// For framework use only. + /// + [JSInvokable(nameof(GetApplyUpdateCapabilities))] + public static string GetApplyUpdateCapabilities() + => GetAgent()?.Capabilities ?? ""; +} diff --git a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index d8152894e88b..06fe2d17b714 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -22,6 +22,8 @@ + + @@ -66,8 +68,4 @@ - - - - From d563672c3f1f6fb6c342f1bd013561d61cfbd70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 15 Jul 2025 17:40:12 +0200 Subject: [PATCH 06/13] Switch between built-in and referenced hotreload --- ....DotNet.HotReload.WebAssembly.lib.module.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js index 3b7387040838..1954e7d04a55 100644 --- a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js +++ b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js @@ -1,4 +1,22 @@ +export async function onRuntimeConfigLoaded(config) { + const value = "false"; + debugger; + config.environmentVariables["__BLAZOR_WEBASSEMBLY_LEGACY_HOTRELOAD"] = value; +} + export async function onRuntimeReady({ getAssemblyExports }) { const exports = await getAssemblyExports("Microsoft.DotNet.HotReload.WebAssembly"); await exports.Microsoft.DotNet.HotReload.WebAssembly.Interop.InitializeAsync(document.baseURI); + + if (!window.Blazor) { + window.Blazor = {}; + } + + window.Blazor._internal.applyHotReloadDeltas = (deltas, loggingLevel) => { + return DotNet.invokeMethod('Microsoft.DotNet.HotReload.WebAssembly', 'ApplyHotReloadDeltas', deltas, loggingLevel) ?? []; + }; + + window.Blazor._internal.getApplyUpdateCapabilities = () => { + return exports.Microsoft.DotNet.HotReload.WebAssembly.Interop.GetApplyUpdateCapabilities() ?? ''; + }; } From 42bd33094b171c391d8dab2c664cb6cc4c7dd555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 15 Jul 2025 20:23:54 +0200 Subject: [PATCH 07/13] Replace blazor interop with runtime --- .../HotReload/src/WebAssemblyHotReload.cs | 25 +++++++++++++++++-- ...DotNet.HotReload.WebAssembly.lib.module.js | 7 +++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs index cc2e735e1da8..21da90f991fd 100644 --- a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs +++ b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices.JavaScript; using System.Runtime.Versioning; using System.Text.Json; -using Microsoft.JSInterop; +using System.Text.Json.Serialization; #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member @@ -144,7 +144,6 @@ public static void ApplyHotReloadDelta(string moduleIdString, byte[] metadataDel /// /// For framework use only. /// - [JSInvokable(nameof(ApplyHotReloadDeltas))] public static LogEntry[] ApplyHotReloadDeltas(Delta[] deltas, int loggingLevel) { var agent = GetAgent(); @@ -165,6 +164,8 @@ public static string GetApplyUpdateCapabilities() internal static partial class Interop { + private static readonly WebAssemblyHotReloadJsonSerializerContext jsonContext = new(new(JsonSerializerDefaults.Web)); + [JSExport] [SupportedOSPlatform("browser")] public static Task InitializeAsync(string baseUri) @@ -183,4 +184,24 @@ public static string GetApplyUpdateCapabilities() { return WebAssemblyHotReload.GetApplyUpdateCapabilities(); } + + [JSExport] + [SupportedOSPlatform("browser")] + public static string ApplyHotReloadDeltas(string deltasJson, int loggingLevel) + { + var deltas = JsonSerializer.Deserialize(deltasJson, jsonContext.DeltaArray); + if (deltas == null) + { + return null; + } + + var result = WebAssemblyHotReload.ApplyHotReloadDeltas(deltas, loggingLevel); + return result == null ? null : JsonSerializer.Serialize(result, jsonContext.LogEntryArray); + } +} + +[JsonSerializable(typeof(WebAssemblyHotReload.Delta[]))] +[JsonSerializable(typeof(WebAssemblyHotReload.LogEntry[]))] +internal sealed partial class WebAssemblyHotReloadJsonSerializerContext : JsonSerializerContext +{ } diff --git a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js index 1954e7d04a55..4ce74e0e1d91 100644 --- a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js +++ b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js @@ -1,7 +1,5 @@ export async function onRuntimeConfigLoaded(config) { - const value = "false"; - debugger; - config.environmentVariables["__BLAZOR_WEBASSEMBLY_LEGACY_HOTRELOAD"] = value; + config.environmentVariables["__BLAZOR_WEBASSEMBLY_LEGACY_HOTRELOAD"] = "false"; } export async function onRuntimeReady({ getAssemblyExports }) { @@ -13,7 +11,8 @@ export async function onRuntimeReady({ getAssemblyExports }) { } window.Blazor._internal.applyHotReloadDeltas = (deltas, loggingLevel) => { - return DotNet.invokeMethod('Microsoft.DotNet.HotReload.WebAssembly', 'ApplyHotReloadDeltas', deltas, loggingLevel) ?? []; + const result = exports.Microsoft.DotNet.HotReload.WebAssembly.Interop.ApplyHotReloadDeltas(JSON.stringify(deltas), loggingLevel); + return result ? JSON.parse(result) : []; }; window.Blazor._internal.getApplyUpdateCapabilities = () => { From fe273524f81c7b22c20601f707e69ee287380301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 16 Jul 2025 09:40:58 +0200 Subject: [PATCH 08/13] Feedback --- .../HotReload/src/PublicAPI.Unshipped.txt | 22 ------------------- .../HotReload/src/WebAssemblyHotReload.cs | 2 +- ...DotNet.HotReload.WebAssembly.lib.module.js | 1 + 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt b/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt index 9d38efd36c49..7dc5c58110bf 100644 --- a/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt +++ b/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt @@ -1,23 +1 @@ #nullable enable -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload -static Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.ApplyHotReloadDelta(string! moduleIdString, byte[]! metadataDelta, byte[]! ilDelta, byte[]! pdbBytes, int[]? updatedTypes) -> void -static Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.GetApplyUpdateCapabilities() -> string! -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.Delta() -> void -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.ILDelta.get -> byte[]! -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.ILDelta.init -> void -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.MetadataDelta.get -> byte[]! -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.MetadataDelta.init -> void -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.ModuleId.get -> string! -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.ModuleId.init -> void -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.PdbDelta.get -> byte[]! -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.PdbDelta.init -> void -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.UpdatedTypes.get -> int[]! -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta.UpdatedTypes.init -> void -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.LogEntry() -> void -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.Message.get -> string! -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.Message.init -> void -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.Severity.get -> int -Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry.Severity.init -> void -static Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.ApplyHotReloadDeltas(Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.Delta[]! deltas, int loggingLevel) -> Microsoft.DotNet.HotReload.WebAssembly.WebAssemblyHotReload.LogEntry[]! diff --git a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs index 21da90f991fd..fc082ee1c245 100644 --- a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs +++ b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs @@ -25,7 +25,7 @@ namespace Microsoft.DotNet.HotReload.WebAssembly; "Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Hot Reload does not support trimming")] -public static partial class WebAssemblyHotReload +internal static partial class WebAssemblyHotReload { /// /// For framework use only. diff --git a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js index 4ce74e0e1d91..fa418d859bcf 100644 --- a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js +++ b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js @@ -1,4 +1,5 @@ export async function onRuntimeConfigLoaded(config) { + // Disable HotReload built-into the Blazor WebAssembly runtime config.environmentVariables["__BLAZOR_WEBASSEMBLY_LEGACY_HOTRELOAD"] = "false"; } From ce74cf84ec41c2e32913c925f69907d8366418ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 16 Jul 2025 10:44:37 +0200 Subject: [PATCH 09/13] Rename --- AspNetCore.slnx | 2 +- eng/Baseline.xml | 2 +- eng/ProjectReferences.props | 2 +- src/Components/Components.slnf | 2 +- ...tNet.HotReload.WebAssembly.Browser.csproj} | 0 .../HotReload/src/WebAssemblyHotReload.cs | 48 ++++--------------- ...tReload.WebAssembly.Browser.lib.module.js} | 8 ++-- ...t.AspNetCore.Components.WebAssembly.csproj | 2 +- .../WebAssembly/src/PublicAPI.Shipped.txt | 3 ++ .../WebAssembly/src/PublicAPI.Unshipped.txt | 20 ++++++++ 10 files changed, 40 insertions(+), 49 deletions(-) rename src/Components/WebAssembly/HotReload/src/{Microsoft.DotNet.HotReload.WebAssembly.csproj => Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj} (100%) rename src/Components/WebAssembly/HotReload/src/wwwroot/{Microsoft.DotNet.HotReload.WebAssembly.lib.module.js => Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js} (64%) diff --git a/AspNetCore.slnx b/AspNetCore.slnx index 41974de988ad..8ee22b3868cd 100644 --- a/AspNetCore.slnx +++ b/AspNetCore.slnx @@ -110,7 +110,7 @@ - + diff --git a/eng/Baseline.xml b/eng/Baseline.xml index 64f7c64e94fc..dc50f294c7a9 100644 --- a/eng/Baseline.xml +++ b/eng/Baseline.xml @@ -42,7 +42,7 @@ Update this list when preparing for a new patch. - + diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index a689a7bb2cbb..f57781e098e1 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -154,7 +154,7 @@ - + diff --git a/src/Components/Components.slnf b/src/Components/Components.slnf index 6a47b56b2929..f99f94b89ce2 100644 --- a/src/Components/Components.slnf +++ b/src/Components/Components.slnf @@ -37,7 +37,7 @@ "src\\Components\\WebAssembly\\WebAssembly.Authentication\\test\\Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj", "src\\Components\\WebAssembly\\WebAssembly\\src\\Microsoft.AspNetCore.Components.WebAssembly.csproj", "src\\Components\\WebAssembly\\WebAssembly\\test\\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj", - "src\\Components\\WebAssembly\\HotReload\\src\\Microsoft.DotNet.HotReload.WebAssembly.csproj", + "src\\Components\\WebAssembly\\HotReload\\src\\Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj", "src\\Components\\WebAssembly\\testassets\\CustomBasePathApp\\CustomBasePathApp.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj", diff --git a/src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.csproj b/src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj similarity index 100% rename from src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.csproj rename to src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj diff --git a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs index fc082ee1c245..4e53a230c7a8 100644 --- a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs +++ b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs @@ -14,7 +14,7 @@ #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member -namespace Microsoft.DotNet.HotReload.WebAssembly; +namespace Microsoft.DotNet.HotReload.WebAssembly.Browser; /// /// Contains methods called by interop. Intended for framework use only, not supported for use in application @@ -63,9 +63,11 @@ public readonly struct Delta private static bool s_initialized; private static HotReloadAgent? s_hotReloadAgent; - internal static async Task InitializeAsync(string baseUri) + [JSExport] + [SupportedOSPlatform("browser")] + public static async Task InitializeAsync(string baseUri) { - if (Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") == "true" && + if (MetadataUpdater.IsSupported && Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") == "true" && OperatingSystem.IsBrowser()) { s_initialized = true; @@ -131,20 +133,7 @@ private static async ValueTask ApplyPreviousDeltasAsync(HotReloadAgent agent, st private static HotReloadAgent? GetAgent() => s_hotReloadAgent ?? (s_initialized ? throw new InvalidOperationException("Hot Reload agent not initialized") : null); - /// - /// For framework use only. - /// - [Obsolete("Use ApplyHotReloadDeltas instead")] - public static void ApplyHotReloadDelta(string moduleIdString, byte[] metadataDelta, byte[] ilDelta, byte[] pdbBytes, int[]? updatedTypes) - { - GetAgent()?.ApplyDeltas( - [new UpdateDelta(Guid.Parse(moduleIdString, CultureInfo.InvariantCulture), metadataDelta, ilDelta, pdbBytes, updatedTypes ?? [])]); - } - - /// - /// For framework use only. - /// - public static LogEntry[] ApplyHotReloadDeltas(Delta[] deltas, int loggingLevel) + private static LogEntry[] ApplyHotReloadDeltas(Delta[] deltas, int loggingLevel) { var agent = GetAgent(); @@ -155,34 +144,13 @@ public static LogEntry[] ApplyHotReloadDeltas(Delta[] deltas, int loggingLevel) .Select(log => new LogEntry() { Message = log.message, Severity = (int)log.severity }).ToArray(); } - /// - /// For framework use only. - /// - public static string GetApplyUpdateCapabilities() - => GetAgent()?.Capabilities ?? ""; -} - -internal static partial class Interop -{ private static readonly WebAssemblyHotReloadJsonSerializerContext jsonContext = new(new(JsonSerializerDefaults.Web)); - [JSExport] - [SupportedOSPlatform("browser")] - public static Task InitializeAsync(string baseUri) - { - if (MetadataUpdater.IsSupported) - { - return WebAssemblyHotReload.InitializeAsync(baseUri); - } - - return Task.CompletedTask; - } - [JSExport] [SupportedOSPlatform("browser")] public static string GetApplyUpdateCapabilities() { - return WebAssemblyHotReload.GetApplyUpdateCapabilities(); + return GetAgent()?.Capabilities ?? ""; } [JSExport] @@ -195,7 +163,7 @@ public static string ApplyHotReloadDeltas(string deltasJson, int loggingLevel) return null; } - var result = WebAssemblyHotReload.ApplyHotReloadDeltas(deltas, loggingLevel); + var result = ApplyHotReloadDeltas(deltas, loggingLevel); return result == null ? null : JsonSerializer.Serialize(result, jsonContext.LogEntryArray); } } diff --git a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js similarity index 64% rename from src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js rename to src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js index fa418d859bcf..91e8d9656384 100644 --- a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.lib.module.js +++ b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js @@ -4,19 +4,19 @@ export async function onRuntimeConfigLoaded(config) { } export async function onRuntimeReady({ getAssemblyExports }) { - const exports = await getAssemblyExports("Microsoft.DotNet.HotReload.WebAssembly"); - await exports.Microsoft.DotNet.HotReload.WebAssembly.Interop.InitializeAsync(document.baseURI); + const exports = await getAssemblyExports("Microsoft.DotNet.HotReload.WebAssembly.Browser"); + await exports.Microsoft.DotNet.HotReload.WebAssembly.Browser.WebAssemblyHotReload.InitializeAsync(document.baseURI); if (!window.Blazor) { window.Blazor = {}; } window.Blazor._internal.applyHotReloadDeltas = (deltas, loggingLevel) => { - const result = exports.Microsoft.DotNet.HotReload.WebAssembly.Interop.ApplyHotReloadDeltas(JSON.stringify(deltas), loggingLevel); + const result = exports.Microsoft.DotNet.HotReload.WebAssembly.Browser.WebAssemblyHotReload.ApplyHotReloadDeltas(JSON.stringify(deltas), loggingLevel); return result ? JSON.parse(result) : []; }; window.Blazor._internal.getApplyUpdateCapabilities = () => { - return exports.Microsoft.DotNet.HotReload.WebAssembly.Interop.GetApplyUpdateCapabilities() ?? ''; + return exports.Microsoft.DotNet.HotReload.WebAssembly.Browser.WebAssemblyHotReload.GetApplyUpdateCapabilities() ?? ''; }; } diff --git a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index 06fe2d17b714..792ac04f73b9 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -21,7 +21,7 @@ - + diff --git a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt index 4a00bf96a2a1..61755ac3eb86 100644 --- a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt +++ b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt @@ -80,6 +80,7 @@ Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration.this[string! key].set -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration.WebAssemblyHostConfiguration() -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache.Default = 0 -> Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache.ForceCache = 4 -> Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache @@ -106,6 +107,8 @@ static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnviro static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsEnvironment(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment, string! environmentName) -> bool static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsProduction(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment) -> bool static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsStaging(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment) -> bool +static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.ApplyHotReloadDelta(string! moduleIdString, byte[]! metadataDelta, byte[]! ilDelta, byte[]! pdbBytes, int[]? updatedTypes) -> void +static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.GetApplyUpdateCapabilities() -> string! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestCache(this System.Net.Http.HttpRequestMessage! requestMessage, Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache requestCache) -> System.Net.Http.HttpRequestMessage! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestCredentials(this System.Net.Http.HttpRequestMessage! requestMessage, Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCredentials requestCredentials) -> System.Net.Http.HttpRequestMessage! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestIntegrity(this System.Net.Http.HttpRequestMessage! requestMessage, string! integrity) -> System.Net.Http.HttpRequestMessage! diff --git a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt index 2514233abd89..3bf453c729a2 100644 --- a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt +++ b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt @@ -1,4 +1,24 @@ +#nullable enable Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.UseServiceProviderOptions(Microsoft.Extensions.DependencyInjection.ServiceProviderOptions! options) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.Delta() -> void +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ILDelta.get -> byte[]! +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ILDelta.init -> void +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.MetadataDelta.get -> byte[]! +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.MetadataDelta.init -> void +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ModuleId.get -> string! +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ModuleId.init -> void +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.PdbDelta.get -> byte[]! +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.PdbDelta.init -> void +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.UpdatedTypes.get -> int[]! +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.UpdatedTypes.init -> void +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.LogEntry() -> void +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Message.get -> string! +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Message.init -> void +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Severity.get -> int +Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Severity.init -> void +static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.ApplyHotReloadDeltas(Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta[]! deltas, int loggingLevel) -> Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry[]! From 89f002f2525c07758fe0dd8ba633fb5610efba7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 16 Jul 2025 10:46:37 +0200 Subject: [PATCH 10/13] Mono environment variables --- ....DotNet.HotReload.WebAssembly.Browser.lib.module.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js index 91e8d9656384..78d312a1e3c2 100644 --- a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js +++ b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js @@ -1,4 +1,14 @@ export async function onRuntimeConfigLoaded(config) { + // If we have 'aspnetcore-browser-refresh', configure mono runtime for HotReload. + if (config.debugLevel !== 0 && globalThis.window?.document?.querySelector("script[src*='aspnetcore-browser-refresh']")) { + if (!config.environmentVariables["DOTNET_MODIFIABLE_ASSEMBLIES"]) { + config.environmentVariables["DOTNET_MODIFIABLE_ASSEMBLIES"] = "debug"; + } + if (!config.environmentVariables["__ASPNETCORE_BROWSER_TOOLS"]) { + config.environmentVariables["__ASPNETCORE_BROWSER_TOOLS"] = "true"; + } + } + // Disable HotReload built-into the Blazor WebAssembly runtime config.environmentVariables["__BLAZOR_WEBASSEMBLY_LEGACY_HOTRELOAD"] = "false"; } From 338635bbfef23d391e6592fd8c7e6655ebbfa336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 16 Jul 2025 14:27:19 +0200 Subject: [PATCH 11/13] Move the package to SDK --- AspNetCore.slnx | 3 - eng/Baseline.xml | 1 - eng/ProjectReferences.props | 1 - src/Components/Components.slnf | 1 - ...otNet.HotReload.WebAssembly.Browser.csproj | 30 --- .../HotReload/src/PublicAPI.Shipped.txt | 0 .../HotReload/src/PublicAPI.Unshipped.txt | 1 - .../HotReload/src/WebAssemblyHotReload.cs | 175 ------------------ ...otReload.WebAssembly.Browser.lib.module.js | 32 ---- ...t.AspNetCore.Components.WebAssembly.csproj | 1 - 10 files changed, 245 deletions(-) delete mode 100644 src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj delete mode 100644 src/Components/WebAssembly/HotReload/src/PublicAPI.Shipped.txt delete mode 100644 src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt delete mode 100644 src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs delete mode 100644 src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js diff --git a/AspNetCore.slnx b/AspNetCore.slnx index 8ee22b3868cd..8d589e5d0a3e 100644 --- a/AspNetCore.slnx +++ b/AspNetCore.slnx @@ -109,9 +109,6 @@ - - - diff --git a/eng/Baseline.xml b/eng/Baseline.xml index dc50f294c7a9..1b4ce55e8e33 100644 --- a/eng/Baseline.xml +++ b/eng/Baseline.xml @@ -42,7 +42,6 @@ Update this list when preparing for a new patch. - diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index f57781e098e1..4dcee4af652b 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -154,7 +154,6 @@ - diff --git a/src/Components/Components.slnf b/src/Components/Components.slnf index f99f94b89ce2..aa83a0f8a9ef 100644 --- a/src/Components/Components.slnf +++ b/src/Components/Components.slnf @@ -37,7 +37,6 @@ "src\\Components\\WebAssembly\\WebAssembly.Authentication\\test\\Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj", "src\\Components\\WebAssembly\\WebAssembly\\src\\Microsoft.AspNetCore.Components.WebAssembly.csproj", "src\\Components\\WebAssembly\\WebAssembly\\test\\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj", - "src\\Components\\WebAssembly\\HotReload\\src\\Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj", "src\\Components\\WebAssembly\\testassets\\CustomBasePathApp\\CustomBasePathApp.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj", diff --git a/src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj b/src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj deleted file mode 100644 index f78226e5191a..000000000000 --- a/src/Components/WebAssembly/HotReload/src/Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - $(DefaultNetCoreTargetFramework) - HotReload package for WebAssembly - $(NoWarn);BL0006 - - $(NoWarn);CS8603 - true - enable - true - true - - false - - - - - - - - - - - - - - - - diff --git a/src/Components/WebAssembly/HotReload/src/PublicAPI.Shipped.txt b/src/Components/WebAssembly/HotReload/src/PublicAPI.Shipped.txt deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt b/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt deleted file mode 100644 index 7dc5c58110bf..000000000000 --- a/src/Components/WebAssembly/HotReload/src/PublicAPI.Unshipped.txt +++ /dev/null @@ -1 +0,0 @@ -#nullable enable diff --git a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs b/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs deleted file mode 100644 index 4e53a230c7a8..000000000000 --- a/src/Components/WebAssembly/HotReload/src/WebAssemblyHotReload.cs +++ /dev/null @@ -1,175 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq; -using System.Net.Http; -using System.Reflection.Metadata; -using System.Runtime.InteropServices.JavaScript; -using System.Runtime.Versioning; -using System.Text.Json; -using System.Text.Json.Serialization; - -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - -namespace Microsoft.DotNet.HotReload.WebAssembly.Browser; - -/// -/// Contains methods called by interop. Intended for framework use only, not supported for use in application -/// code. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -[UnconditionalSuppressMessage( - "Trimming", - "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", - Justification = "Hot Reload does not support trimming")] -internal static partial class WebAssemblyHotReload -{ - /// - /// For framework use only. - /// - public readonly struct LogEntry - { - public string Message { get; init; } - public int Severity { get; init; } - } - - /// - /// For framework use only. - /// - internal sealed class Update - { - public int Id { get; set; } - public Delta[] Deltas { get; set; } = default!; - } - - /// - /// For framework use only. - /// - public readonly struct Delta - { - public string ModuleId { get; init; } - public byte[] MetadataDelta { get; init; } - public byte[] ILDelta { get; init; } - public byte[] PdbDelta { get; init; } - public int[] UpdatedTypes { get; init; } - } - - private static readonly AgentReporter s_reporter = new(); - private static readonly JsonSerializerOptions s_jsonSerializerOptions = new(JsonSerializerDefaults.Web); - - private static bool s_initialized; - private static HotReloadAgent? s_hotReloadAgent; - - [JSExport] - [SupportedOSPlatform("browser")] - public static async Task InitializeAsync(string baseUri) - { - if (MetadataUpdater.IsSupported && Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") == "true" && - OperatingSystem.IsBrowser()) - { - s_initialized = true; - - var agent = new HotReloadAgent(); - - var existingAgent = Interlocked.CompareExchange(ref s_hotReloadAgent, agent, null); - if (existingAgent != null) - { - throw new InvalidOperationException("Hot Reload agent already initialized"); - } - - await ApplyPreviousDeltasAsync(agent, baseUri); - } - } - - private static async ValueTask ApplyPreviousDeltasAsync(HotReloadAgent agent, string baseUri) - { - string errorMessage; - - using var client = new HttpClient() - { - BaseAddress = new Uri(baseUri, UriKind.Absolute) - }; - - try - { - var response = await client.GetAsync("/_framework/blazor-hotreload"); - if (response.IsSuccessStatusCode) - { - var deltasJson = await response.Content.ReadAsStringAsync(); - var updates = deltasJson != "" ? JsonSerializer.Deserialize(deltasJson, s_jsonSerializerOptions) : null; - if (updates == null) - { - s_reporter.Report($"No previous updates to apply.", AgentMessageSeverity.Verbose); - return; - } - - var i = 1; - foreach (var update in updates) - { - s_reporter.Report($"Reapplying update {i}/{updates.Length}.", AgentMessageSeverity.Verbose); - - agent.ApplyDeltas( - update.Deltas.Select(d => new UpdateDelta(Guid.Parse(d.ModuleId, CultureInfo.InvariantCulture), d.MetadataDelta, d.ILDelta, d.PdbDelta, d.UpdatedTypes))); - - i++; - } - - return; - } - - errorMessage = $"HTTP GET '/_framework/blazor-hotreload' returned {response.StatusCode}"; - } - catch (Exception e) - { - errorMessage = e.ToString(); - } - - s_reporter.Report($"Failed to retrieve and apply previous deltas from the server: ${errorMessage}", AgentMessageSeverity.Error); - } - - private static HotReloadAgent? GetAgent() - => s_hotReloadAgent ?? (s_initialized ? throw new InvalidOperationException("Hot Reload agent not initialized") : null); - - private static LogEntry[] ApplyHotReloadDeltas(Delta[] deltas, int loggingLevel) - { - var agent = GetAgent(); - - agent?.ApplyDeltas( - deltas.Select(d => new UpdateDelta(Guid.Parse(d.ModuleId, CultureInfo.InvariantCulture), d.MetadataDelta, d.ILDelta, d.PdbDelta, d.UpdatedTypes))); - - return s_reporter.GetAndClearLogEntries((ResponseLoggingLevel)loggingLevel) - .Select(log => new LogEntry() { Message = log.message, Severity = (int)log.severity }).ToArray(); - } - - private static readonly WebAssemblyHotReloadJsonSerializerContext jsonContext = new(new(JsonSerializerDefaults.Web)); - - [JSExport] - [SupportedOSPlatform("browser")] - public static string GetApplyUpdateCapabilities() - { - return GetAgent()?.Capabilities ?? ""; - } - - [JSExport] - [SupportedOSPlatform("browser")] - public static string ApplyHotReloadDeltas(string deltasJson, int loggingLevel) - { - var deltas = JsonSerializer.Deserialize(deltasJson, jsonContext.DeltaArray); - if (deltas == null) - { - return null; - } - - var result = ApplyHotReloadDeltas(deltas, loggingLevel); - return result == null ? null : JsonSerializer.Serialize(result, jsonContext.LogEntryArray); - } -} - -[JsonSerializable(typeof(WebAssemblyHotReload.Delta[]))] -[JsonSerializable(typeof(WebAssemblyHotReload.LogEntry[]))] -internal sealed partial class WebAssemblyHotReloadJsonSerializerContext : JsonSerializerContext -{ -} diff --git a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js b/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js deleted file mode 100644 index 78d312a1e3c2..000000000000 --- a/src/Components/WebAssembly/HotReload/src/wwwroot/Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js +++ /dev/null @@ -1,32 +0,0 @@ -export async function onRuntimeConfigLoaded(config) { - // If we have 'aspnetcore-browser-refresh', configure mono runtime for HotReload. - if (config.debugLevel !== 0 && globalThis.window?.document?.querySelector("script[src*='aspnetcore-browser-refresh']")) { - if (!config.environmentVariables["DOTNET_MODIFIABLE_ASSEMBLIES"]) { - config.environmentVariables["DOTNET_MODIFIABLE_ASSEMBLIES"] = "debug"; - } - if (!config.environmentVariables["__ASPNETCORE_BROWSER_TOOLS"]) { - config.environmentVariables["__ASPNETCORE_BROWSER_TOOLS"] = "true"; - } - } - - // Disable HotReload built-into the Blazor WebAssembly runtime - config.environmentVariables["__BLAZOR_WEBASSEMBLY_LEGACY_HOTRELOAD"] = "false"; -} - -export async function onRuntimeReady({ getAssemblyExports }) { - const exports = await getAssemblyExports("Microsoft.DotNet.HotReload.WebAssembly.Browser"); - await exports.Microsoft.DotNet.HotReload.WebAssembly.Browser.WebAssemblyHotReload.InitializeAsync(document.baseURI); - - if (!window.Blazor) { - window.Blazor = {}; - } - - window.Blazor._internal.applyHotReloadDeltas = (deltas, loggingLevel) => { - const result = exports.Microsoft.DotNet.HotReload.WebAssembly.Browser.WebAssemblyHotReload.ApplyHotReloadDeltas(JSON.stringify(deltas), loggingLevel); - return result ? JSON.parse(result) : []; - }; - - window.Blazor._internal.getApplyUpdateCapabilities = () => { - return exports.Microsoft.DotNet.HotReload.WebAssembly.Browser.WebAssemblyHotReload.GetApplyUpdateCapabilities() ?? ''; - }; -} diff --git a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index 792ac04f73b9..957925b03276 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -21,7 +21,6 @@ - From fa32e2f887e852f2c3dc93bb73ff6ab97c16c113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 16 Jul 2025 15:17:43 +0200 Subject: [PATCH 12/13] Clean up --- AspNetCore.slnx | 15 +++++++++++---- .../WebAssembly/src/PublicAPI.Unshipped.txt | 4 ---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/AspNetCore.slnx b/AspNetCore.slnx index 8d589e5d0a3e..b73adbbb4b85 100644 --- a/AspNetCore.slnx +++ b/AspNetCore.slnx @@ -409,8 +409,6 @@ - - @@ -1143,10 +1141,20 @@ + + + + + + + + + + @@ -1190,8 +1198,8 @@ - + @@ -1217,7 +1225,6 @@ - diff --git a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt index 3bf453c729a2..f9836324fe1f 100644 --- a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt +++ b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt @@ -1,8 +1,4 @@ #nullable enable -Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.UseServiceProviderOptions(Microsoft.Extensions.DependencyInjection.ServiceProviderOptions! options) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! -Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions -static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! -static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.Delta() -> void Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ILDelta.get -> byte[]! From 26ecd04fe41eccdb0d9da2cc101b132e73db29c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 16 Jul 2025 15:19:07 +0200 Subject: [PATCH 13/13] Clean up --- AspNetCore.slnx | 13 +++---------- .../WebAssembly/src/PublicAPI.Unshipped.txt | 4 ++++ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/AspNetCore.slnx b/AspNetCore.slnx index b73adbbb4b85..60f5ca0d74fc 100644 --- a/AspNetCore.slnx +++ b/AspNetCore.slnx @@ -409,6 +409,8 @@ + + @@ -1141,20 +1143,10 @@ - - - - - - - - - - @@ -1225,6 +1217,7 @@ + diff --git a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt index f9836324fe1f..fc7b59f8f640 100644 --- a/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt +++ b/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ #nullable enable +Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.UseServiceProviderOptions(Microsoft.Extensions.DependencyInjection.ServiceProviderOptions! options) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! +Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.Delta() -> void Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta.ILDelta.get -> byte[]! @@ -17,4 +19,6 @@ Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEn Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Message.init -> void Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Severity.get -> int Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry.Severity.init -> void +static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! +static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.ApplyHotReloadDeltas(Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.Delta[]! deltas, int loggingLevel) -> Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.LogEntry[]!