diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
index 64539a49fdaa8..1219d25e02ff3 100644
--- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
+++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
@@ -19,9 +19,13 @@ Copyright (c) .NET Foundation. All rights reserved.
dotnet
$([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory), '..', 'WasmAppHost'))
- <_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(OutputPath), '$(AssemblyName).runtimeconfig.json'))
+
+ <_RunWorkingDirectory>$(OutputPath)
+ <_RunWorkingDirectory Condition="'$(_RunWorkingDirectory)' != '' and !$([System.IO.Path]::IsPathRooted($(_RunWorkingDirectory)))">$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(_RunWorkingDirectory)))
+ <_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(_RunWorkingDirectory), '$(AssemblyName).runtimeconfig.json'))
+
exec "$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))" --use-staticwebassets --runtime-config "$(_RuntimeConfigJsonPath)" $(WasmHostArguments)
- $(OutputPath)
+ $(_RunWorkingDirectory)
diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs
index 76a64c9fe4e76..08488d487a615 100644
--- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs
@@ -39,7 +39,7 @@ private void UpdateProgramCS()
private void UpdateBrowserMainJs(string targetFramework, string runtimeAssetsRelativePath = DefaultRuntimeAssetsRelativePath)
{
- string mainJsPath = Path.Combine(_projectDir!, "main.js");
+ string mainJsPath = Path.Combine(_projectDir!, "wwwroot", "main.js");
string mainJsContent = File.ReadAllText(mainJsPath);
// .withExitOnUnhandledError() is available only only >net7.0
@@ -408,11 +408,12 @@ public void ConsolePublishAndRun(string config, bool aot, bool relinking)
[Theory]
[InlineData("", BuildTestBase.DefaultTargetFramework, DefaultRuntimeAssetsRelativePath)]
- [InlineData("", BuildTestBase.DefaultTargetFramework, "./")]
+ // [ActiveIssue("https://github.com/dotnet/runtime/issues/90979")]
+ // [InlineData("", BuildTestBase.DefaultTargetFramework, "./")]
+ // [InlineData("-f net8.0", "net8.0", "./")]
// [ActiveIssue("https://github.com/dotnet/runtime/issues/79313")]
// [InlineData("-f net7.0", "net7.0")]
[InlineData("-f net8.0", "net8.0", DefaultRuntimeAssetsRelativePath)]
- [InlineData("-f net8.0", "net8.0", "./")]
public async Task BrowserBuildAndRun(string extraNewArgs, string targetFramework, string runtimeAssetsRelativePath)
{
string config = "Debug";
diff --git a/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs b/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs
index 1f70aa46df8fa..9ff2155c68e60 100644
--- a/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs
@@ -57,6 +57,21 @@ protected override IReadOnlySet GetDotNetFilesExpectedSet(AssertBundleOp
return res;
}
+
+ public void AssertBundle(BuildArgs buildArgs, BuildProjectOptions buildProjectOptions)
+ {
+ AssertBundle(new(
+ Config: buildArgs.Config,
+ IsPublish: buildProjectOptions.Publish,
+ TargetFramework: buildProjectOptions.TargetFramework,
+ BinFrameworkDir: buildProjectOptions.BinFrameworkDir ?? FindBinFrameworkDir(buildArgs.Config, buildProjectOptions.Publish, buildProjectOptions.TargetFramework),
+ PredefinedIcudt: buildProjectOptions.PredefinedIcudt,
+ GlobalizationMode: buildProjectOptions.GlobalizationMode,
+ AssertSymbolsFile: false,
+ ExpectedFileType: buildProjectOptions.Publish && buildArgs.Config == "Release" ? NativeFilesType.Relinked : NativeFilesType.FromRuntimePack
+ ));
+ }
+
public void AssertBundle(AssertWasmSdkBundleOptions assertOptions)
{
IReadOnlyDictionary actualDotnetFiles = AssertBasicBundle(assertOptions);
diff --git a/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs
index a76aaee38a929..6280a022c1a42 100644
--- a/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs
@@ -59,19 +59,23 @@ public string CreateWasmTemplateProject(string id, string template = "wasmbrowse
public (string projectDir, string buildOutput) BuildTemplateProject(BuildArgs buildArgs,
string id,
- BuildProjectOptions buildProjectOptions,
- AssertTestMainJsAppBundleOptions? assertAppBundleOptions = null)
+ BuildProjectOptions buildProjectOptions)
{
(CommandResult res, string logFilePath) = BuildProjectWithoutAssert(id, buildArgs.Config, buildProjectOptions);
if (buildProjectOptions.UseCache)
_buildContext.CacheBuild(buildArgs, new BuildProduct(_projectDir!, logFilePath, true, res.Output));
if (buildProjectOptions.AssertAppBundle)
- AssertBundle(buildArgs, buildProjectOptions, res.Output, assertAppBundleOptions);
+ {
+ if (buildProjectOptions.IsBrowserProject)
+ AssertWasmSdkBundle(buildArgs, buildProjectOptions, res.Output);
+ else
+ AssertTestMainJsBundle(buildArgs, buildProjectOptions, res.Output);
+ }
return (_projectDir!, res.Output);
}
- public void AssertBundle(BuildArgs buildArgs,
+ public void AssertTestMainJsBundle(BuildArgs buildArgs,
BuildProjectOptions buildProjectOptions,
string? buildOutput = null,
AssertTestMainJsAppBundleOptions? assertAppBundleOptions = null)
@@ -79,11 +83,25 @@ public void AssertBundle(BuildArgs buildArgs,
if (buildOutput is not null)
ProjectProviderBase.AssertRuntimePackPath(buildOutput, buildProjectOptions.TargetFramework ?? DefaultTargetFramework);
- // TODO: templates don't use wasm sdk yet
var testMainJsProvider = new TestMainJsProjectProvider(_testOutput, _projectDir!);
if (assertAppBundleOptions is not null)
testMainJsProvider.AssertBundle(assertAppBundleOptions);
else
testMainJsProvider.AssertBundle(buildArgs, buildProjectOptions);
}
+
+ public void AssertWasmSdkBundle(BuildArgs buildArgs,
+ BuildProjectOptions buildProjectOptions,
+ string? buildOutput = null,
+ AssertWasmSdkBundleOptions? assertAppBundleOptions = null)
+ {
+ if (buildOutput is not null)
+ ProjectProviderBase.AssertRuntimePackPath(buildOutput, buildProjectOptions.TargetFramework ?? DefaultTargetFramework);
+
+ var projectProvider = new WasmSdkBasedProjectProvider(_testOutput, _projectDir!);
+ if (assertAppBundleOptions is not null)
+ projectProvider.AssertBundle(assertAppBundleOptions);
+ else
+ projectProvider.AssertBundle(buildArgs, buildProjectOptions);
+ }
}
diff --git a/src/mono/wasm/host/BrowserHost.cs b/src/mono/wasm/host/BrowserHost.cs
index 5c16a420e76de..adb24f81f88da 100644
--- a/src/mono/wasm/host/BrowserHost.cs
+++ b/src/mono/wasm/host/BrowserHost.cs
@@ -167,7 +167,7 @@ private static DevServerOptions CreateDevServerOptions(BrowserArguments args, st
devServerOptions = CreateDevServerOptions(urls, staticWebAssetsPath, onConsoleConnected);
if (devServerOptions == null)
- throw new CommandLineException("Please, provide mainAssembly in hostProperties of runtimeconfig");
+ throw new CommandLineException($"Please, provide mainAssembly in hostProperties of runtimeconfig. Alternatively leave the static web assets manifest ('*{staticWebAssetsV2Extension}') in the build output directory '{appPath}' .");
}
return devServerOptions;
@@ -183,7 +183,7 @@ private static DevServerOptions CreateDevServerOptions(BrowserArguments args, st
);
private static string? FindFirstFileWithExtension(string directory, string extension)
- => Directory.EnumerateFiles(directory, "*" + extension).First();
+ => Directory.EnumerateFiles(directory, "*" + extension).FirstOrDefault();
private async Task RunConsoleMessagesPump(WebSocket socket, WasmTestMessagesProcessor messagesProcessor, CancellationToken token)
{
diff --git a/src/mono/wasm/host/DevServer/DevServer.cs b/src/mono/wasm/host/DevServer/DevServer.cs
index b1369deabcc0c..1acbe6954eeb3 100644
--- a/src/mono/wasm/host/DevServer/DevServer.cs
+++ b/src/mono/wasm/host/DevServer/DevServer.cs
@@ -70,8 +70,7 @@ private static IConfiguration ConfigureHostConfiguration(DevServerOptions option
[WebHostDefaults.EnvironmentKey] = "Development",
["Logging:LogLevel:Microsoft"] = "Warning",
["Logging:LogLevel:Microsoft.Hosting.Lifetime"] = "Information",
- [WebHostDefaults.StaticWebAssetsKey] = options.StaticWebAssetsPath,
- ["ApplyCopHeaders"] = options.WebServerUseCrossOriginPolicy.ToString()
+ [WebHostDefaults.StaticWebAssetsKey] = options.StaticWebAssetsPath
};
config.AddInMemoryCollection(inMemoryConfiguration);
diff --git a/src/mono/wasm/host/DevServer/DevServerStartup.cs b/src/mono/wasm/host/DevServer/DevServerStartup.cs
index f438caf4b4b7a..0fdc45a1754fd 100644
--- a/src/mono/wasm/host/DevServer/DevServerStartup.cs
+++ b/src/mono/wasm/host/DevServer/DevServerStartup.cs
@@ -2,13 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
+using System.Net.WebSockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Microsoft.WebAssembly.AppHost;
namespace Microsoft.WebAssembly.AppHost.DevServer;
@@ -27,16 +30,16 @@ public static void ConfigureServices(IServiceCollection services)
services.AddRouting();
}
- public static void Configure(IApplicationBuilder app, TaskCompletionSource realUrlsAvailableTcs, ILogger logger, IHostApplicationLifetime applicationLifetime, IConfiguration configuration)
+ public static void Configure(IApplicationBuilder app, IOptions optionsContainer, TaskCompletionSource realUrlsAvailableTcs, ILogger logger, IHostApplicationLifetime applicationLifetime, IConfiguration configuration)
{
app.UseDeveloperExceptionPage();
EnableConfiguredPathbase(app, configuration);
app.UseWebAssemblyDebugging();
- bool applyCopHeaders = configuration.GetValue("ApplyCopHeaders");
+ DevServerOptions options = optionsContainer.Value;
- if (applyCopHeaders)
+ if (options.WebServerUseCrossOriginPolicy)
{
app.Use(async (ctx, next) =>
{
@@ -63,6 +66,29 @@ public static void Configure(IApplicationBuilder app, TaskCompletionSource
+ {
+ if (ctx.Request.Path.StartsWithSegments("/console"))
+ {
+ if (!ctx.WebSockets.IsWebSocketRequest)
+ {
+ ctx.Response.StatusCode = 400;
+ return;
+ }
+
+ using WebSocket socket = await ctx.WebSockets.AcceptWebSocketAsync();
+ await options.OnConsoleConnected(socket);
+ }
+ else
+ {
+ await next(ctx);
+ }
+ });
+ }
app.UseEndpoints(endpoints =>
{
@@ -70,7 +96,7 @@ public static void Configure(IApplicationBuilder app, TaskCompletionSource
{
- if (applyCopHeaders)
+ if (options.WebServerUseCrossOriginPolicy)
{
// Browser multi-threaded runtime requires cross-origin policy headers to enable SharedArrayBuffer.
ApplyCrossOriginPolicyHeaders(fileContext.Context);
diff --git a/src/mono/wasm/host/Program.cs b/src/mono/wasm/host/Program.cs
index a5005dce9d0fc..105eb59929eca 100644
--- a/src/mono/wasm/host/Program.cs
+++ b/src/mono/wasm/host/Program.cs
@@ -30,6 +30,9 @@ public static async Task Main(string[] args)
RegisterHostHandler(WasmHost.Wasmtime, WasiEngineHost.InvokeAsync);
using CancellationTokenSource cts = new();
+
+ Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel();
+
ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
builder
.AddPassThroughConsole()
diff --git a/src/mono/wasm/templates/templates/browser/README.md b/src/mono/wasm/templates/templates/browser/README.md
deleted file mode 100644
index 7ddf4fd1bce9b..0000000000000
--- a/src/mono/wasm/templates/templates/browser/README.md
+++ /dev/null
@@ -1,26 +0,0 @@
-## .NET WebAssembly Browser app
-
-## Build
-
-You can build the app from Visual Studio or from the command-line:
-
-```
-dotnet build -c Debug/Release
-```
-
-After building the app, the result is in the `bin/$(Configuration)/net7.0/browser-wasm/AppBundle` directory.
-
-## Run
-
-You can build the app from Visual Studio or the command-line:
-
-```
-dotnet run -c Debug/Release
-```
-
-Or you can start any static file server from the AppBundle directory:
-
-```
-dotnet tool install dotnet-serve
-dotnet serve -d:bin/$(Configuration)/net7.0/browser-wasm/AppBundle
-```
\ No newline at end of file
diff --git a/src/mono/wasm/templates/templates/browser/browser.0.csproj b/src/mono/wasm/templates/templates/browser/browser.0.csproj
index 401bdae24fab8..588c521958212 100644
--- a/src/mono/wasm/templates/templates/browser/browser.0.csproj
+++ b/src/mono/wasm/templates/templates/browser/browser.0.csproj
@@ -1,13 +1,6 @@
-
+
- net7.0
- browser-wasm
- Exe
+ net8.0
true
-
-
-
-
-
diff --git a/src/mono/wasm/templates/templates/browser/runtimeconfig.template.json b/src/mono/wasm/templates/templates/browser/runtimeconfig.template.json
index 8f0557352c6ed..b96a94320ba5e 100644
--- a/src/mono/wasm/templates/templates/browser/runtimeconfig.template.json
+++ b/src/mono/wasm/templates/templates/browser/runtimeconfig.template.json
@@ -3,9 +3,8 @@
"perHostConfig": [
{
"name": "browser",
- "html-path": "index.html",
- "Host": "browser"
+ "host": "browser"
}
]
}
-}
+}
\ No newline at end of file
diff --git a/src/mono/wasm/templates/templates/browser/index.html b/src/mono/wasm/templates/templates/browser/wwwroot/index.html
similarity index 100%
rename from src/mono/wasm/templates/templates/browser/index.html
rename to src/mono/wasm/templates/templates/browser/wwwroot/index.html
diff --git a/src/mono/wasm/templates/templates/browser/main.js b/src/mono/wasm/templates/templates/browser/wwwroot/main.js
similarity index 100%
rename from src/mono/wasm/templates/templates/browser/main.js
rename to src/mono/wasm/templates/templates/browser/wwwroot/main.js