diff --git a/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs index c9f7177d2852..9d3c61515bf8 100644 --- a/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs +++ b/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs @@ -63,7 +63,7 @@ private static ProjectKey GetProjectKey(ProjectGraphNode projectNode) ProcessSpec processSpec, EnvironmentVariablesBuilder environmentBuilder, ProjectOptions projectOptions, - HotReloadProfile profile, + HotReloadAppModel appModel, CancellationToken cancellationToken) { BrowserRefreshServer? server; @@ -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); } } @@ -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; } diff --git a/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs index fe56edee2085..f7718adc1d60 100644 --- a/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs +++ b/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs @@ -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); diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyAppModel.cs new file mode 100644 index 000000000000..b666406f2a91 --- /dev/null +++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyAppModel.cs @@ -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 +{ + /// + /// Blazor client-only WebAssembly app. + /// + internal sealed class BlazorWebAssemblyAppModel(ProjectGraphNode clientProject) : HotReloadAppModel + { + public override bool RequiresBrowserRefresh => true; + + /// + /// Blazor WASM does not need dotnet applier as all changes are applied in the browser, + /// the process being launched is a dev server. + /// + 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); + } + } +} diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs index 617d7d733856..dd78c35eb51e 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs @@ -48,7 +48,7 @@ public override Task> 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 { @@ -61,7 +61,7 @@ public override Task> GetApplyUpdateCapabilitiesAsync(Can } else { - Reporter.Verbose($"Project specifies capabilities."); + Reporter.Verbose($"Project '{project.GetDisplayName()}' specifies capabilities: '{string.Join(' ', capabilities)}'"); } return Task.FromResult(capabilities); diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedAppModel.cs new file mode 100644 index 000000000000..94f8a467b408 --- /dev/null +++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedAppModel.cs @@ -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 +{ + /// + /// Blazor WebAssembly app hosted by an ASP.NET Core app. + /// App has a client and server projects and deltas are applied to both processes. + /// + 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); + } + } +} diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs index 2499e85e13c1..d48289e6fe99 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs @@ -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() diff --git a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs index b14beb923a4c..d245229d2e55 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs @@ -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 TrackRunningProjectAsync( ProjectGraphNode projectNode, ProjectOptions projectOptions, - HotReloadProfile profile, + HotReloadAppModel appModel, string namedPipeName, BrowserRefreshServer? browserRefreshServer, ProcessSpec processSpec, @@ -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 diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DefaultAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/DefaultAppModel.cs new file mode 100644 index 000000000000..b104de3c263a --- /dev/null +++ b/src/BuiltInTools/dotnet-watch/HotReload/DefaultAppModel.cs @@ -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; + +/// +/// Default model. +/// +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); +} diff --git a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadAppModel.cs new file mode 100644 index 000000000000..595c0a52b4a2 --- /dev/null +++ b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadAppModel.cs @@ -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; } + + /// + /// True to inject delta applier to the process. + /// + 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(); + queue.Enqueue(projectNode); + + ProjectInstance? aspnetCoreProject = null; + + var visited = new HashSet(); + + 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; + } +} diff --git a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs deleted file mode 100644 index 5fec43f115ba..000000000000 --- a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs +++ /dev/null @@ -1,28 +0,0 @@ -// 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; - -internal enum HotReloadProfile -{ - Default, - - /// - /// Blazor WebAssembly app - /// - BlazorWebAssembly, - - /// - /// Blazor WebAssembly app hosted by an ASP.NET Core app. - /// - BlazorHosted, -} - -internal static class HotReloadProfileExtensions -{ - extension(HotReloadProfile profile) - { - public bool RequiresBrowserRefresh - => profile is HotReloadProfile.BlazorWebAssembly or HotReloadProfile.BlazorHosted; - } -} diff --git a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfileReader.cs b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfileReader.cs deleted file mode 100644 index 28461c6f45f4..000000000000 --- a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfileReader.cs +++ /dev/null @@ -1,64 +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 Microsoft.Build.Execution; -using Microsoft.Build.Graph; - -namespace Microsoft.DotNet.Watch -{ - internal static class HotReloadProfileReader - { - public static HotReloadProfile InferHotReloadProfile(ProjectGraphNode projectNode, IReporter reporter) - { - if (projectNode.IsWebApp()) - { - var queue = new Queue(); - queue.Enqueue(projectNode); - - ProjectInstance? aspnetCoreProject = null; - - var visited = new HashSet(); - - 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 he a blazor hosted app. - if (aspnetCoreProject is not null && aspnetCoreProject != currentNode.ProjectInstance) - { - reporter.Verbose($"HotReloadProfile: BlazorHosted. {aspnetCoreProject.FullPath} references BlazorWebAssembly project {currentNode.ProjectInstance.FullPath}.", emoji: "🔥"); - return HotReloadProfile.BlazorHosted; - } - - reporter.Verbose("HotReloadProfile: BlazorWebAssembly.", emoji: "🔥"); - return HotReloadProfile.BlazorWebAssembly; - } - } - - foreach (var project in currentNode.ProjectReferences) - { - if (visited.Add(project)) - { - queue.Enqueue(project); - } - } - } - } - - reporter.Verbose("HotReloadProfile: Default.", emoji: "🔥"); - return HotReloadProfile.Default; - } - } -} diff --git a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs index e42aa9bd1ae1..288bf04b4d5b 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs @@ -42,11 +42,7 @@ public EnvironmentOptions EnvironmentOptions return null; } - var profile = HotReloadProfileReader.InferHotReloadProfile(projectNode, Reporter); - - // Blazor WASM does not need dotnet applier as all changes are applied in the browser, - // the process being launched is a dev server. - var injectDeltaApplier = profile != HotReloadProfile.BlazorWebAssembly; + var appModel = HotReloadAppModel.InferFromProject(projectNode, Reporter); var processSpec = new ProcessSpec { @@ -81,7 +77,7 @@ public EnvironmentOptions EnvironmentOptions // https://github.com/dotnet/runtime/blob/342936c5a88653f0f622e9d6cb727a0e59279b31/src/mono/browser/runtime/loader/config.ts#L330 environmentBuilder.SetVariable(EnvironmentVariables.Names.DotNetModifiableAssemblies, "debug"); - if (injectDeltaApplier) + if (appModel.InjectDeltaApplier) { // HotReload startup hook should be loaded before any other startup hooks: environmentBuilder.DotNetStartupHooks.Insert(0, DeltaApplier.StartupHookPath); @@ -94,7 +90,7 @@ public EnvironmentOptions EnvironmentOptions } } - var browserRefreshServer = await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectNode, processSpec, environmentBuilder, projectOptions, profile, cancellationToken); + var browserRefreshServer = await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectNode, processSpec, environmentBuilder, projectOptions, appModel, cancellationToken); var arguments = new List() { @@ -117,7 +113,7 @@ public EnvironmentOptions EnvironmentOptions return await compilationHandler.TrackRunningProjectAsync( projectNode, projectOptions, - profile, + appModel, namedPipeName, browserRefreshServer, processSpec, diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/Program.cs b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/Program.cs new file mode 100644 index 000000000000..6d5184961182 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/Program.cs @@ -0,0 +1,8 @@ +using System; +using System.Threading.Tasks; + +while (true) +{ + Console.WriteLine("."); + await Task.Delay(1000); +} diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/Properties/launchSettings.json b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/Properties/launchSettings.json new file mode 100644 index 000000000000..dc7b2f31b4b3 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/blazorhosted.csproj b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/blazorhosted.csproj new file mode 100644 index 000000000000..8f74e9ad79d5 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/blazorhosted.csproj @@ -0,0 +1,10 @@ + + + + $(CurrentTargetFramework) + + + + + + diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/App.razor b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/App.razor new file mode 100644 index 000000000000..13f3043f0c49 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/App.razor @@ -0,0 +1,8 @@ + + + + + +

Sorry, there's nothing at this address.

+
+
diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/LinkToWebRoot/css/app.css b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/LinkToWebRoot/css/app.css new file mode 100644 index 000000000000..6c9631b4e1dd --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/LinkToWebRoot/css/app.css @@ -0,0 +1 @@ +.publish { } diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/Pages/Index.razor b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/Pages/Index.razor new file mode 100644 index 000000000000..16dac3192520 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/Pages/Index.razor @@ -0,0 +1,5 @@ +@page "/" + +

Hello, world!

+ +Welcome to your new app. diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/Program.cs b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/Program.cs new file mode 100644 index 000000000000..4b20dea9ded0 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/Program.cs @@ -0,0 +1,16 @@ +using System; + +namespace standalone +{ + public class Program + { + public static void Main(string[] args) + { + GC.KeepAlive(typeof(System.Text.Json.JsonSerializer)); + GC.KeepAlive(typeof(RazorClassLibrary.Class1)); +#if REFERENCE_classlibrarywithsatelliteassemblies + GC.KeepAlive(typeof(classlibrarywithsatelliteassemblies.Class1)); +#endif + } + } +} diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/_Imports.razor b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/_Imports.razor new file mode 100644 index 000000000000..129b440e8600 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/_Imports.razor @@ -0,0 +1,2 @@ +@using Microsoft.AspNetCore.Components.Routing +@using standalone diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/blazorwasm.csproj b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/blazorwasm.csproj new file mode 100644 index 000000000000..967d9b18c81d --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/blazorwasm.csproj @@ -0,0 +1,39 @@ + + + + $(CurrentTargetFramework) + custom-service-worker-assets.js + + + + + + + + + + + + + + + wwwroot\ + + + + + + + + + + + + + + + <_BlazorBrotliCompressionLevel>NoCompression + + + diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/icudt_custom.dat b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/icudt_custom.dat new file mode 100644 index 000000000000..703d9704375e Binary files /dev/null and b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/icudt_custom.dat differ diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/Fake-License.txt b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/Fake-License.txt new file mode 100644 index 000000000000..5f282702bb03 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/Fake-License.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/css/app.css b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/css/app.css new file mode 100644 index 000000000000..fc64a1237602 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/css/app.css @@ -0,0 +1 @@ +.build { } diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/index.html b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/index.html new file mode 100644 index 000000000000..bbfa66c41abc --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/index.html @@ -0,0 +1,24 @@ + + + + + + + standalone + + + + + + + Loading... + +
+ An unhandled error has occurred. + Reload + 🗙 +
+ + + + diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js new file mode 100644 index 000000000000..a2ecc1b349c2 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js @@ -0,0 +1 @@ +// This is the production service worker diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/serviceworkers/my-service-worker.js b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/serviceworkers/my-service-worker.js new file mode 100644 index 000000000000..c42d1c84755f --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorwasm/wwwroot/serviceworkers/my-service-worker.js @@ -0,0 +1 @@ +// This is the development service worker diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/Class1.cs b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/Class1.cs new file mode 100644 index 000000000000..fb55605ff46f --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/Class1.cs @@ -0,0 +1,6 @@ +namespace RazorClassLibrary +{ + public class Class1 + { + } +} diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/RazorClassLibrary.csproj b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/RazorClassLibrary.csproj new file mode 100644 index 000000000000..933dad1f1ee9 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/RazorClassLibrary.csproj @@ -0,0 +1,8 @@ + + + + $(CurrentTargetFramework) + true + + + diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/wwwroot/styles.css b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/wwwroot/styles.css new file mode 100644 index 000000000000..5f282702bb03 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/wwwroot/styles.css @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js b/test/TestAssets/TestProjects/WatchBlazorWasmHosted/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs index e8ed1cb1c4e9..a91aeb3c2792 100644 --- a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs +++ b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs @@ -524,6 +524,30 @@ public async Task BlazorWasm_Restart() await App.WaitUntilOutputContains($"dotnet watch ⌚ Reloading browser."); } + [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307") + public async Task BlazorWasmHosted() + { + var testAsset = TestAssets.CopyTestAsset("WatchBlazorWasmHosted") + .WithSource(); + + var tfm = ToolsetInfo.CurrentTargetFramework; + + var port = TestOptions.GetTestPort(); + App.Start(testAsset, ["--urls", "http://localhost:" + port], "blazorhosted", testFlags: TestFlags.MockBrowser); + + await App.AssertWaitingForChanges(); + + App.AssertOutputContains(MessageDescriptor.ConfiguredToUseBrowserRefresh); + App.AssertOutputContains(MessageDescriptor.ConfiguredToLaunchBrowser); + App.AssertOutputContains("dotnet watch 🔥 HotReloadProfile: BlazorHosted"); + + // client capabilities: + App.AssertOutputContains($"dotnet watch ⌚ [blazorhosted ({tfm})] Project 'blazorwasm ({tfm})' specifies capabilities: 'Baseline AddMethodToExistingType AddStaticFieldToExistingType NewTypeDefinition ChangeCustomAttributes AddInstanceFieldToExistingType GenericAddMethodToExistingType GenericUpdateMethod UpdateParameters GenericAddFieldToExistingType'"); + + // server capabilities: + App.AssertOutputContains($"dotnet watch ⌚ [blazorhosted ({tfm})] Capabilities: 'Baseline AddMethodToExistingType AddStaticFieldToExistingType AddInstanceFieldToExistingType NewTypeDefinition ChangeCustomAttributes UpdateParameters GenericUpdateMethod GenericAddMethodToExistingType GenericAddFieldToExistingType AddFieldRva'"); + } + [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307") public async Task Razor_Component_ScopedCssAndStaticAssets() {