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.