diff --git a/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs index 38315cb09ca3..c9f7177d2852 100644 --- a/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs +++ b/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs @@ -63,6 +63,7 @@ private static ProjectKey GetProjectKey(ProjectGraphNode projectNode) ProcessSpec processSpec, EnvironmentVariablesBuilder environmentBuilder, ProjectOptions projectOptions, + HotReloadProfile profile, CancellationToken cancellationToken) { BrowserRefreshServer? server; @@ -75,7 +76,7 @@ private static ProjectKey GetProjectKey(ProjectGraphNode projectNode) hasExistingServer = _servers.TryGetValue(key, out server); if (!hasExistingServer) { - server = IsServerSupported(projectNode) ? new BrowserRefreshServer(context.EnvironmentOptions, context.Reporter) : null; + server = IsServerSupported(projectNode, profile) ? new BrowserRefreshServer(context.EnvironmentOptions, context.Reporter) : null; _servers.Add(key, server); } } @@ -271,25 +272,24 @@ private bool CanLaunchBrowser(DotNetWatchContext context, ProjectGraphNode proje return true; } - public bool IsServerSupported(ProjectGraphNode projectNode) + public bool IsServerSupported(ProjectGraphNode projectNode, HotReloadProfile profile) { if (context.EnvironmentOptions.SuppressBrowserRefresh) { + context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_SuppressedViaEnvironmentVariable.ToErrorWhen(profile.RequiresBrowserRefresh), EnvironmentVariables.SuppressBrowserRefresh); return false; } if (!projectNode.IsNetCoreApp(minVersion: s_minimumSupportedVersion)) { - context.Reporter.Warn( - "Skipping configuring browser-refresh middleware since the target framework version is not supported." + - " For more information see 'https://aka.ms/dotnet/watch/unsupported-tfm'."); - + context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported.ToErrorWhen(profile.RequiresBrowserRefresh)); return false; } - if (!IsWebApp(projectNode)) + // We only want to enable browser refresh if this is a WebApp (ASP.NET Core / Blazor app). + if (!projectNode.IsWebApp()) { - context.Reporter.Verbose("Skipping configuring browser-refresh middleware since this is not a webapp."); + context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_NotWebApp.ToErrorWhen(profile.RequiresBrowserRefresh)); return false; } @@ -297,10 +297,6 @@ public bool IsServerSupported(ProjectGraphNode projectNode) return true; } - // We only want to enable browser refresh if this is a WebApp (ASP.NET Core / Blazor app). - private static bool IsWebApp(ProjectGraphNode projectNode) - => projectNode.GetCapabilities().Any(value => value is "AspNetCore" or "WebAssembly"); - private LaunchSettingsProfile GetLaunchProfile(ProjectOptions projectOptions) { var projectDirectory = Path.GetDirectoryName(projectOptions.ProjectPath); diff --git a/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs index 9fcedd45853c..fe56edee2085 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, shutdownCancellationToken) + ? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, HotReloadProfile.Default, shutdownCancellationToken) : null; environmentBuilder.SetProcessEnvironmentVariables(processSpec); diff --git a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs index c9015c61dcb4..2f8786a5d6ff 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs @@ -102,13 +102,21 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken) _reporter.Report(MessageDescriptor.HotReloadSessionStarted); } - private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, ProjectGraphNode project, BrowserRefreshServer? browserRefreshServer, IReporter processReporter) - => profile switch + 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, @@ -125,6 +133,12 @@ private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, Project var projectPath = projectNode.ProjectInstance.FullPath; var deltaApplier = CreateDeltaApplier(profile, projectNode, browserRefreshServer, processReporter); + if (deltaApplier == null) + { + // error already reported + return null; + } + var processExitedSource = new CancellationTokenSource(); var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(processExitedSource.Token, cancellationToken); diff --git a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs index 898148d2f331..5fec43f115ba 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs @@ -1,20 +1,28 @@ // 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 +namespace Microsoft.DotNet.Watch; + +internal enum HotReloadProfile { - internal enum HotReloadProfile - { - Default, + Default, + + /// + /// Blazor WebAssembly app + /// + BlazorWebAssembly, - /// - /// Blazor WebAssembly app - /// - BlazorWebAssembly, + /// + /// Blazor WebAssembly app hosted by an ASP.NET Core app. + /// + BlazorHosted, +} - /// - /// 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 index fe8e9a968e01..28461c6f45f4 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfileReader.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfileReader.cs @@ -11,44 +11,48 @@ internal static class HotReloadProfileReader { public static HotReloadProfile InferHotReloadProfile(ProjectGraphNode projectNode, IReporter reporter) { - var queue = new Queue(); - queue.Enqueue(projectNode); - - ProjectInstance? aspnetCoreProject = null; + if (projectNode.IsWebApp()) + { + var queue = new Queue(); + queue.Enqueue(projectNode); - var visited = new HashSet(); + ProjectInstance? aspnetCoreProject = null; - while (queue.Count > 0) - { - var currentNode = queue.Dequeue(); - var projectCapability = currentNode.ProjectInstance.GetItems("ProjectCapability"); + var visited = new HashSet(); - foreach (var item in projectCapability) + while (queue.Count > 0) { - if (item.EvaluatedInclude == "AspNetCore") - { - aspnetCoreProject = currentNode.ProjectInstance; - break; - } - else if (item.EvaluatedInclude == "WebAssembly") + var currentNode = queue.Dequeue(); + var projectCapability = currentNode.ProjectInstance.GetItems("ProjectCapability"); + + foreach (var item in projectCapability) { - // We saw a previous project that was AspNetCore. This must he a blazor hosted app. - if (aspnetCoreProject is not null && aspnetCoreProject != currentNode.ProjectInstance) + if (item.EvaluatedInclude == "AspNetCore") { - reporter.Verbose($"HotReloadProfile: BlazorHosted. {aspnetCoreProject.FullPath} references BlazorWebAssembly project {currentNode.ProjectInstance.FullPath}.", emoji: "🔥"); - return HotReloadProfile.BlazorHosted; + aspnetCoreProject = currentNode.ProjectInstance; + break; } - reporter.Verbose("HotReloadProfile: BlazorWebAssembly.", emoji: "🔥"); - return HotReloadProfile.BlazorWebAssembly; + 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)) + foreach (var project in currentNode.ProjectReferences) { - queue.Enqueue(project); + if (visited.Add(project)) + { + queue.Enqueue(project); + } } } } diff --git a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs index 48f19b0fc010..e42aa9bd1ae1 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs @@ -94,7 +94,7 @@ public EnvironmentOptions EnvironmentOptions } } - var browserRefreshServer = await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectNode, processSpec, environmentBuilder, projectOptions, cancellationToken); + var browserRefreshServer = await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectNode, processSpec, environmentBuilder, projectOptions, profile, cancellationToken); var arguments = new List() { diff --git a/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs b/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs index 282c77205ee0..84053d6fe410 100644 --- a/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs +++ b/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs @@ -48,36 +48,51 @@ public bool TryGetMessage(string? prefix, object?[] args, [NotNullWhen(true)] ou return true; } + public const string WarningEmoji = "⚠"; + public const string ErrorEmoji = "❌"; + public const string HotReloadEmoji = "🔥"; + public const string WatchEmoji = "⌚"; + public const string StopEmoji = "🛑"; + public const string RestartEmoji = "🔄"; + public const string LaunchEmoji = "🚀"; + public const string WaitEmoji = "⏳"; + + public MessageDescriptor ToErrorWhen(bool condition) + => condition ? this with { Severity = MessageSeverity.Error, Emoji = ErrorEmoji } : this; + // predefined messages used for testing: public static readonly MessageDescriptor HotReloadSessionStarting = new(Format: null, Emoji: null, MessageSeverity.None, s_id++); - public static readonly MessageDescriptor HotReloadSessionStarted = new("Hot reload session started.", "🔥", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor ProjectBaselinesUpdated = new("Project baselines updated.", "🔥", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor FixBuildError = new("Fix the error to continue or press Ctrl+C to exit.", "⌚", MessageSeverity.Warning, s_id++); - public static readonly MessageDescriptor WaitingForChanges = new("Waiting for changes", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor LaunchedProcess = new("Launched '{0}' with arguments '{1}': process id {2}", "🚀", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor KillingProcess = new("Killing process {0}", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor HotReloadChangeHandled = new("Hot reload change handled in {0}ms.", "🔥", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor HotReloadSucceeded = new("Hot reload succeeded.", "🔥", MessageSeverity.Output, s_id++); - public static readonly MessageDescriptor UpdatesApplied = new("Updates applied: {0} out of {1}.", "🔥", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor WaitingForFileChangeBeforeRestarting = new("Waiting for a file to change before restarting ...", "⏳", MessageSeverity.Warning, s_id++); - public static readonly MessageDescriptor WatchingWithHotReload = new("Watching with Hot Reload.", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor RestartInProgress = new("Restart in progress.", "🔄", MessageSeverity.Output, s_id++); - public static readonly MessageDescriptor RestartRequested = new("Restart requested.", "🔄", MessageSeverity.Output, s_id++); - public static readonly MessageDescriptor ShutdownRequested = new("Shutdown requested. Press Ctrl+C again to force exit.", "🛑", MessageSeverity.Output, s_id++); - public static readonly MessageDescriptor ApplyUpdate_Error = new("{0}", "❌", MessageSeverity.Error, s_id++); - public static readonly MessageDescriptor ApplyUpdate_Warning = new("{0}", "⚠", MessageSeverity.Warning, s_id++); - public static readonly MessageDescriptor ApplyUpdate_Verbose = new("{0}", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor ApplyUpdate_ChangingEntryPoint = new("{0} Press \"Ctrl + R\" to restart.", "⚠", MessageSeverity.Warning, s_id++); - public static readonly MessageDescriptor ApplyUpdate_FileContentDoesNotMatchBuiltSource = new("{0} Expected if a source file is updated that is linked to project whose build is not up-to-date.", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor ConfiguredToLaunchBrowser = new("dotnet-watch is configured to launch a browser on ASP.NET Core application startup.", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor ConfiguredToUseBrowserRefresh = new("Configuring the app to use browser-refresh middleware", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor IgnoringChangeInHiddenDirectory = new("Ignoring change in hidden directory '{0}': {1} '{2}'", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor IgnoringChangeInOutputDirectory = new("Ignoring change in output directory: {0} '{1}'", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor FileAdditionTriggeredReEvaluation = new("File addition triggered re-evaluation.", "⌚", MessageSeverity.Verbose, s_id++); - public static readonly MessageDescriptor NoCSharpChangesToApply = new("No C# changes to apply.", "⌚", MessageSeverity.Output, s_id++); - public static readonly MessageDescriptor Exited = new("Exited", "⌚", MessageSeverity.Output, s_id++); - public static readonly MessageDescriptor ExitedWithUnknownErrorCode = new("Exited with unknown error code", "❌", MessageSeverity.Error, s_id++); - public static readonly MessageDescriptor ExitedWithErrorCode = new("Exited with error code {0}", "❌", MessageSeverity.Error, s_id++); + public static readonly MessageDescriptor HotReloadSessionStarted = new("Hot reload session started.", HotReloadEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor ProjectBaselinesUpdated = new("Project baselines updated.", HotReloadEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor FixBuildError = new("Fix the error to continue or press Ctrl+C to exit.", WatchEmoji, MessageSeverity.Warning, s_id++); + public static readonly MessageDescriptor WaitingForChanges = new("Waiting for changes", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor LaunchedProcess = new("Launched '{0}' with arguments '{1}': process id {2}", LaunchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor KillingProcess = new("Killing process {0}", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor HotReloadChangeHandled = new("Hot reload change handled in {0}ms.", HotReloadEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor HotReloadSucceeded = new("Hot reload succeeded.", HotReloadEmoji, MessageSeverity.Output, s_id++); + public static readonly MessageDescriptor UpdatesApplied = new("Updates applied: {0} out of {1}.", HotReloadEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor WaitingForFileChangeBeforeRestarting = new("Waiting for a file to change before restarting ...", WaitEmoji, MessageSeverity.Warning, s_id++); + public static readonly MessageDescriptor WatchingWithHotReload = new("Watching with Hot Reload.", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor RestartInProgress = new("Restart in progress.", RestartEmoji, MessageSeverity.Output, s_id++); + public static readonly MessageDescriptor RestartRequested = new("Restart requested.", RestartEmoji, MessageSeverity.Output, s_id++); + public static readonly MessageDescriptor ShutdownRequested = new("Shutdown requested. Press Ctrl+C again to force exit.", StopEmoji, MessageSeverity.Output, s_id++); + public static readonly MessageDescriptor ApplyUpdate_Error = new("{0}", ErrorEmoji, MessageSeverity.Error, s_id++); + public static readonly MessageDescriptor ApplyUpdate_Warning = new("{0}", WarningEmoji, MessageSeverity.Warning, s_id++); + public static readonly MessageDescriptor ApplyUpdate_Verbose = new("{0}", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor ApplyUpdate_ChangingEntryPoint = new("{0} Press \"Ctrl + R\" to restart.", WarningEmoji, MessageSeverity.Warning, s_id++); + public static readonly MessageDescriptor ApplyUpdate_FileContentDoesNotMatchBuiltSource = new("{0} Expected if a source file is updated that is linked to project whose build is not up-to-date.", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor ConfiguredToLaunchBrowser = new("dotnet-watch is configured to launch a browser on ASP.NET Core application startup.", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor ConfiguredToUseBrowserRefresh = new("Configuring the app to use browser-refresh middleware", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor IgnoringChangeInHiddenDirectory = new("Ignoring change in hidden directory '{0}': {1} '{2}'", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor IgnoringChangeInOutputDirectory = new("Ignoring change in output directory: {0} '{1}'", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor FileAdditionTriggeredReEvaluation = new("File addition triggered re-evaluation.", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor NoCSharpChangesToApply = new("No C# changes to apply.", WatchEmoji, MessageSeverity.Output, s_id++); + public static readonly MessageDescriptor Exited = new("Exited", WatchEmoji, MessageSeverity.Output, s_id++); + public static readonly MessageDescriptor ExitedWithUnknownErrorCode = new("Exited with unknown error code", ErrorEmoji, MessageSeverity.Error, s_id++); + public static readonly MessageDescriptor ExitedWithErrorCode = new("Exited with error code {0}", ErrorEmoji, MessageSeverity.Error, s_id++); + public static readonly MessageDescriptor SkippingConfiguringBrowserRefresh_SuppressedViaEnvironmentVariable = new("Skipping configuring browser-refresh middleware since its refresh server suppressed via environment variable {0}.", WatchEmoji, MessageSeverity.Verbose, s_id++); + public static readonly MessageDescriptor SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported = new("Skipping configuring browser-refresh middleware since the target framework version is not supported. For more information see 'https://aka.ms/dotnet/watch/unsupported-tfm'.", WatchEmoji, MessageSeverity.Warning, s_id++); + public static readonly MessageDescriptor SkippingConfiguringBrowserRefresh_NotWebApp = new("Skipping configuring browser-refresh middleware since this is not a webapp.", WatchEmoji, MessageSeverity.Verbose, s_id++); } internal interface IReporter @@ -105,16 +120,16 @@ public bool PrefixProcessOutput void Report(MessageDescriptor descriptor, params object?[] args) => Report(descriptor, prefix: "", args); - void Verbose(string message, string emoji = "⌚") + void Verbose(string message, string emoji = MessageDescriptor.WatchEmoji) => Report(new MessageDescriptor(message, emoji, MessageSeverity.Verbose, Id: null)); - void Output(string message, string emoji = "⌚") + void Output(string message, string emoji = MessageDescriptor.WatchEmoji) => Report(new MessageDescriptor(message, emoji, MessageSeverity.Output, Id: null)); - void Warn(string message, string emoji = "⌚") + void Warn(string message, string emoji = MessageDescriptor.WatchEmoji) => Report(new MessageDescriptor(message, emoji, MessageSeverity.Warning, Id: null)); - void Error(string message, string emoji = "❌") + void Error(string message, string emoji = MessageDescriptor.ErrorEmoji) => Report(new MessageDescriptor(message, emoji, MessageSeverity.Error, Id: null)); } } diff --git a/src/BuiltInTools/dotnet-watch/Utilities/ProjectGraphNodeExtensions.cs b/src/BuiltInTools/dotnet-watch/Utilities/ProjectGraphNodeExtensions.cs index 537115424387..7aa8da4c1b58 100644 --- a/src/BuiltInTools/dotnet-watch/Utilities/ProjectGraphNodeExtensions.cs +++ b/src/BuiltInTools/dotnet-watch/Utilities/ProjectGraphNodeExtensions.cs @@ -33,6 +33,9 @@ public static bool IsNetCoreApp(this ProjectGraphNode projectNode) public static bool IsNetCoreApp(this ProjectGraphNode projectNode, Version minVersion) => IsNetCoreApp(projectNode) && IsTargetFrameworkVersionOrNewer(projectNode, minVersion); + public static bool IsWebApp(this ProjectGraphNode projectNode) + => projectNode.GetCapabilities().Any(static value => value is "AspNetCore" or "WebAssembly"); + public static string? GetOutputDirectory(this ProjectGraphNode projectNode) => projectNode.ProjectInstance.GetPropertyValue("TargetPath") is { Length: >0 } path ? Path.GetDirectoryName(Path.Combine(projectNode.ProjectInstance.Directory, path)) : null; diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/WatchAspire.AppHost.csproj b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/WatchAspire.AppHost.csproj index e1e9ea2abb49..87c2319e1b33 100644 --- a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/WatchAspire.AppHost.csproj +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/WatchAspire.AppHost.csproj @@ -6,11 +6,11 @@ $(CurrentTargetFramework) enable true - ad800ccc-954c-40cc-920b-2e09fc9eee7a + diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/App.razor b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/App.razor new file mode 100644 index 000000000000..13f3043f0c49 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/App.razor @@ -0,0 +1,8 @@ + + + + + +

Sorry, there's nothing at this address.

+
+
diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/Pages/Index.razor b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/Pages/Index.razor new file mode 100644 index 000000000000..16dac3192520 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/Pages/Index.razor @@ -0,0 +1,5 @@ +@page "/" + +

Hello, world!

+ +Welcome to your new app. diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/Program.cs b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/Program.cs new file mode 100644 index 000000000000..3ebadb8b76c0 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/Program.cs @@ -0,0 +1,11 @@ +using System; + +namespace standalone +{ + public class Program + { + public static void Main(string[] args) + { + } + } +} diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/Properties/launchSettings.json b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/Properties/launchSettings.json new file mode 100644 index 000000000000..dc7b2f31b4b3 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/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/WatchAspire/WatchAspire.Wasm/WatchAspire.Wasm.csproj b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/WatchAspire.Wasm.csproj new file mode 100644 index 000000000000..0bea79ac8508 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/WatchAspire.Wasm.csproj @@ -0,0 +1,12 @@ + + + + $(CurrentTargetFramework) + + + + + + + + diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/_Imports.razor b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/_Imports.razor new file mode 100644 index 000000000000..129b440e8600 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/_Imports.razor @@ -0,0 +1,2 @@ +@using Microsoft.AspNetCore.Components.Routing +@using standalone diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/wwwroot/css/app.css b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/wwwroot/css/app.css new file mode 100644 index 000000000000..fc64a1237602 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/wwwroot/css/app.css @@ -0,0 +1 @@ +.build { } diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/wwwroot/index.html b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/wwwroot/index.html new file mode 100644 index 000000000000..bbfa66c41abc --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/wwwroot/index.html @@ -0,0 +1,24 @@ + + + + + + + standalone + + + + + + + Loading... + +
+ An unhandled error has occurred. + Reload + 🗙 +
+ + + +