diff --git a/src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs b/src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs
index a107f6bd66..c1b2359584 100644
--- a/src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs
+++ b/src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs
@@ -12,11 +12,16 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
internal static class ServiceWorkerAssert
{
- internal static void VerifyServiceWorkerFiles(MSBuildResult result, string outputDirectory, string serviceWorkerPath, string serviceWorkerContent, string assetsManifestPath)
+ internal static void VerifyServiceWorkerFiles(MSBuildResult result,
+ string outputDirectory,
+ string serviceWorkerPath,
+ string serviceWorkerContent,
+ string assetsManifestPath,
+ string staticWebAssetsBasePath = "")
{
// Check the expected files are there
- var serviceWorkerResolvedPath = Assert.FileExists(result, outputDirectory, serviceWorkerPath);
- var assetsManifestResolvedPath = Assert.FileExists(result, outputDirectory, assetsManifestPath);
+ var serviceWorkerResolvedPath = Assert.FileExists(result, outputDirectory, staticWebAssetsBasePath, serviceWorkerPath);
+ var assetsManifestResolvedPath = Assert.FileExists(result, outputDirectory, staticWebAssetsBasePath, assetsManifestPath);
// Check the service worker contains the expected content (which comes from the PublishedContent file)
Assert.FileContains(result, serviceWorkerResolvedPath, serviceWorkerContent);
@@ -36,8 +41,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
// We don't list compressed files in the SWAM, as these are transparent to the client,
// nor do we list the service worker itself or its assets manifest, as these don't need to be fetched in the same way
if (IsCompressedFile(relativePath)
- || string.Equals(relativePath, serviceWorkerPath, StringComparison.Ordinal)
- || string.Equals(relativePath, assetsManifestPath, StringComparison.Ordinal))
+ || string.Equals(relativePath, Path.Combine(staticWebAssetsBasePath, serviceWorkerPath), StringComparison.Ordinal)
+ || string.Equals(relativePath, Path.Combine(staticWebAssetsBasePath, assetsManifestPath), StringComparison.Ordinal))
{
continue;
}
diff --git a/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs
index 44142cf35a..57aa766733 100644
--- a/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs
+++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs
@@ -252,6 +252,111 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
VerifyCompression(result, blazorPublishDirectory);
}
+ [Fact]
+ public async Task Publish_WithStaticWebBasePathWorks()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", "razorclasslibrary");
+ project.AddProjectFileContent(
+@"
+ different-path/
+");
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+
+ Assert.BuildPassed(result);
+
+ var publishDirectory = project.PublishOutputDirectory;
+
+ // Verify nothing is published directly to the wwwroot directory
+ Assert.FileCountEquals(result, 0, Path.Combine(publishDirectory, "wwwroot"), "*", SearchOption.TopDirectoryOnly);
+
+ var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot", "different-path");
+
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+
+ // Verify static assets are in the publish directory
+ Assert.FileExists(result, blazorPublishDirectory, "index.html");
+
+ // Verify web.config
+ Assert.FileExists(result, publishDirectory, "web.config");
+ var webConfigContent = new StreamReader(GetType().Assembly.GetManifestResourceStream("Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.BlazorWasm.web.config")).ReadToEnd();
+ Assert.FileContentEquals(result, Path.Combine(publishDirectory, "web.config"), webConfigContent);
+ Assert.FileCountEquals(result, 1, publishDirectory, "*", SearchOption.TopDirectoryOnly);
+
+ // Verify static web assets from referenced projects are copied.
+ Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
+ Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "styles.css");
+
+ VerifyBootManifestHashes(result, blazorPublishDirectory);
+ VerifyServiceWorkerFiles(result,
+ Path.Combine(publishDirectory, "wwwroot"),
+ serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
+ serviceWorkerContent: "// This is the production service worker",
+ assetsManifestPath: "custom-service-worker-assets.js",
+ staticWebAssetsBasePath: "different-path");
+ }
+
+ [Fact]
+ public async Task Publish_Hosted_WithStaticWebBasePathWorks()
+ {
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
+ var wasmProject = project.GetSibling("blazorwasm");
+ wasmProject.AddProjectFileContent(
+@"
+ different-path/
+");
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+
+ Assert.BuildPassed(result);
+
+ var publishDirectory = project.PublishOutputDirectory;
+ // Make sure the main project exists
+ Assert.FileExists(result, publishDirectory, "blazorhosted.dll");
+
+ Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
+ Assert.FileExists(result, publishDirectory, "blazorwasm.dll");
+
+ var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot", "different-path");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
+
+ // Verify project references appear as static web assets
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll");
+ // Also verify project references to the server project appear in the publish output
+ Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
+
+ // Verify static assets are in the publish directory
+ Assert.FileExists(result, blazorPublishDirectory, "index.html");
+
+ // Verify static web assets from referenced projects are copied.
+ Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
+ Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css");
+
+ // Verify web.config
+ Assert.FileExists(result, publishDirectory, "web.config");
+
+ VerifyBootManifestHashes(result, blazorPublishDirectory);
+
+ // Verify compression works
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
+
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.gz");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.gz");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.gz");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.gz");
+ }
+
private static void VerifyCompression(MSBuildResult result, string blazorPublishDirectory)
{
var original = Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets
index 0f9a3d9c2b..e1e0a3c807 100644
--- a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets
+++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets
@@ -74,7 +74,6 @@ Copyright (c) .NET Foundation. All rights reserved.
<_BlazorOutputPath>wwwroot\_framework\
-
@@ -457,6 +456,12 @@ Copyright (c) .NET Foundation. All rights reserved.
+
+ <_BlazorPublishOutputPath Condition="'$(StaticWebAssetBasePath)' != '/'">wwwroot\$(StaticWebAssetBasePath.Replace('/', '\'))
+ <_BlazorPublishOutputPath Condition="'$(StaticWebAssetBasePath)' == '/'">wwwroot\
+ <_BlazorFrameworkPublishPath>$(_BlazorPublishOutputPath)_framework\
+
+
@@ -472,13 +477,24 @@ Copyright (c) .NET Foundation. All rights reserved.
-
-
-
+
+
+
+
+
+
+
@@ -515,11 +531,11 @@ Copyright (c) .NET Foundation. All rights reserved.
+ RelativePath="$(_BlazorFrameworkPublishPath)blazor.boot.json" />
+ RelativePath="$(_BlazorFrameworkPublishPath)%(FileName)%(Extension)" />
diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets
index 5202eb338d..88969337c4 100644
--- a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets
+++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets
@@ -128,6 +128,7 @@ Copyright (c) .NET Foundation. All rights reserved.
%(ServiceWorker.PublishedContent)
%(ServiceWorker.Identity)
%(ServiceWorker.Identity)
+ $(_BlazorPublishOutputPath)$([System.String]::Copy('%(ServiceWorker.Identity)').Substring(8))
<_ServiceWorkerPublishFile Include="@(ResolvedFileToPublish)" Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))">
@@ -161,7 +162,7 @@ Copyright (c) .NET Foundation. All rights reserved.