Skip to content

Commit d9dd46e

Browse files
Use StaticWebAssetBasePath if specified to calculate blazorwasm publish paths (#27896)
In 3.2, Blazor WebAssembly had a feature which allowed prefix the relative path within the PublishDir by configuring a StaticWebAssetBasePath property in the project file. As part of migrating to 5.0, this feature was (accidentally) not brought forward which is remedied by this commit. * Use StaticWebAssetBasePath to calculate asset base paths * Add publish tests to verify hosted and standalone scenarios Fixes #27776 Co-authored-by: Artak <[email protected]>
1 parent 26c2891 commit d9dd46e

File tree

4 files changed

+140
-13
lines changed

4 files changed

+140
-13
lines changed

src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
1212
{
1313
internal static class ServiceWorkerAssert
1414
{
15-
internal static void VerifyServiceWorkerFiles(MSBuildResult result, string outputDirectory, string serviceWorkerPath, string serviceWorkerContent, string assetsManifestPath)
15+
internal static void VerifyServiceWorkerFiles(MSBuildResult result,
16+
string outputDirectory,
17+
string serviceWorkerPath,
18+
string serviceWorkerContent,
19+
string assetsManifestPath,
20+
string staticWebAssetsBasePath = "")
1621
{
1722
// Check the expected files are there
18-
var serviceWorkerResolvedPath = Assert.FileExists(result, outputDirectory, serviceWorkerPath);
19-
var assetsManifestResolvedPath = Assert.FileExists(result, outputDirectory, assetsManifestPath);
23+
var serviceWorkerResolvedPath = Assert.FileExists(result, outputDirectory, staticWebAssetsBasePath, serviceWorkerPath);
24+
var assetsManifestResolvedPath = Assert.FileExists(result, outputDirectory, staticWebAssetsBasePath, assetsManifestPath);
2025

2126
// Check the service worker contains the expected content (which comes from the PublishedContent file)
2227
Assert.FileContains(result, serviceWorkerResolvedPath, serviceWorkerContent);
@@ -36,8 +41,8 @@ internal static void VerifyServiceWorkerFiles(MSBuildResult result, string outpu
3641
// We don't list compressed files in the SWAM, as these are transparent to the client,
3742
// nor do we list the service worker itself or its assets manifest, as these don't need to be fetched in the same way
3843
if (IsCompressedFile(relativePath)
39-
|| string.Equals(relativePath, serviceWorkerPath, StringComparison.Ordinal)
40-
|| string.Equals(relativePath, assetsManifestPath, StringComparison.Ordinal))
44+
|| string.Equals(relativePath, Path.Combine(staticWebAssetsBasePath, serviceWorkerPath), StringComparison.Ordinal)
45+
|| string.Equals(relativePath, Path.Combine(staticWebAssetsBasePath, assetsManifestPath), StringComparison.Ordinal))
4146
{
4247
continue;
4348
}

src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,111 @@ public async Task Publish_WithNoBuild_Works()
252252
VerifyCompression(result, blazorPublishDirectory);
253253
}
254254

255+
[Fact]
256+
public async Task Publish_WithStaticWebBasePathWorks()
257+
{
258+
// Arrange
259+
using var project = ProjectDirectory.Create("blazorwasm", "razorclasslibrary");
260+
project.AddProjectFileContent(
261+
@"<PropertyGroup>
262+
<StaticWebAssetBasePath>different-path/</StaticWebAssetBasePath>
263+
</PropertyGroup>");
264+
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
265+
266+
Assert.BuildPassed(result);
267+
268+
var publishDirectory = project.PublishOutputDirectory;
269+
270+
// Verify nothing is published directly to the wwwroot directory
271+
Assert.FileCountEquals(result, 0, Path.Combine(publishDirectory, "wwwroot"), "*", SearchOption.TopDirectoryOnly);
272+
273+
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot", "different-path");
274+
275+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
276+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
277+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
278+
Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
279+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
280+
281+
// Verify static assets are in the publish directory
282+
Assert.FileExists(result, blazorPublishDirectory, "index.html");
283+
284+
// Verify web.config
285+
Assert.FileExists(result, publishDirectory, "web.config");
286+
var webConfigContent = new StreamReader(GetType().Assembly.GetManifestResourceStream("Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.BlazorWasm.web.config")).ReadToEnd();
287+
Assert.FileContentEquals(result, Path.Combine(publishDirectory, "web.config"), webConfigContent);
288+
Assert.FileCountEquals(result, 1, publishDirectory, "*", SearchOption.TopDirectoryOnly);
289+
290+
// Verify static web assets from referenced projects are copied.
291+
Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
292+
Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "styles.css");
293+
294+
VerifyBootManifestHashes(result, blazorPublishDirectory);
295+
VerifyServiceWorkerFiles(result,
296+
Path.Combine(publishDirectory, "wwwroot"),
297+
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
298+
serviceWorkerContent: "// This is the production service worker",
299+
assetsManifestPath: "custom-service-worker-assets.js",
300+
staticWebAssetsBasePath: "different-path");
301+
}
302+
303+
[Fact]
304+
public async Task Publish_Hosted_WithStaticWebBasePathWorks()
305+
{
306+
using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
307+
var wasmProject = project.GetSibling("blazorwasm");
308+
wasmProject.AddProjectFileContent(
309+
@"<PropertyGroup>
310+
<StaticWebAssetBasePath>different-path/</StaticWebAssetBasePath>
311+
</PropertyGroup>");
312+
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
313+
314+
Assert.BuildPassed(result);
315+
316+
var publishDirectory = project.PublishOutputDirectory;
317+
// Make sure the main project exists
318+
Assert.FileExists(result, publishDirectory, "blazorhosted.dll");
319+
320+
Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
321+
Assert.FileExists(result, publishDirectory, "blazorwasm.dll");
322+
323+
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot", "different-path");
324+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
325+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
326+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
327+
Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
328+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
329+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
330+
331+
// Verify project references appear as static web assets
332+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll");
333+
// Also verify project references to the server project appear in the publish output
334+
Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
335+
336+
// Verify static assets are in the publish directory
337+
Assert.FileExists(result, blazorPublishDirectory, "index.html");
338+
339+
// Verify static web assets from referenced projects are copied.
340+
Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
341+
Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css");
342+
343+
// Verify web.config
344+
Assert.FileExists(result, publishDirectory, "web.config");
345+
346+
VerifyBootManifestHashes(result, blazorPublishDirectory);
347+
348+
// Verify compression works
349+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
350+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br");
351+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
352+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
353+
354+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.gz");
355+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.gz");
356+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.gz");
357+
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.gz");
358+
}
359+
255360
private static void VerifyCompression(MSBuildResult result, string blazorPublishDirectory)
256361
{
257362
var original = Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");

src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ Copyright (c) .NET Foundation. All rights reserved.
7474

7575
<!-- Internal properties -->
7676
<_BlazorOutputPath>wwwroot\_framework\</_BlazorOutputPath>
77-
7877
</PropertyGroup>
7978

8079
<ItemGroup>
@@ -457,6 +456,12 @@ Copyright (c) .NET Foundation. All rights reserved.
457456
<Output TaskParameter="JoinResult" ItemName="_ResolvedSatelliteToPublish" />
458457
</JoinItems>
459458

459+
<PropertyGroup>
460+
<_BlazorPublishOutputPath Condition="'$(StaticWebAssetBasePath)' != '/'">wwwroot\$(StaticWebAssetBasePath.Replace('/', '\'))</_BlazorPublishOutputPath>
461+
<_BlazorPublishOutputPath Condition="'$(StaticWebAssetBasePath)' == '/'">wwwroot\</_BlazorPublishOutputPath>
462+
<_BlazorFrameworkPublishPath>$(_BlazorPublishOutputPath)_framework\</_BlazorFrameworkPublishPath>
463+
</PropertyGroup>
464+
460465
<ItemGroup>
461466
<ResolvedFileToPublish Remove="@(_ResolvedSatelliteToPublish)" />
462467
<ResolvedFileToPublish Include="@(_ResolvedSatelliteToPublish)" />
@@ -472,13 +477,24 @@ Copyright (c) .NET Foundation. All rights reserved.
472477
<!-- Remove dotnet.js from publish output -->
473478
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition="'%(ResolvedFileToPublish.RelativePath)' == 'dotnet.js'" />
474479

480+
<!-- Remove pdbs from the publish output -->
481+
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition="'$(CopyOutputSymbolsToPublishDirectory)' != 'true' AND '%(Extension)' == '.pdb'" />
482+
475483
<!-- Retarget so that items are published to the wwwroot directory -->
484+
<!--
485+
This changes files (such as wwwroot/index.html) that are published to $(PublishDir)wwwroot\ -> $(PublishDir)wwwroot\$(StaticWebAssetBasePath)\.
486+
Ignore any user specified web.config in the process.
487+
-->
476488
<ResolvedFileToPublish
477-
RelativePath="$(_BlazorOutputPath)%(ResolvedFileToPublish.RelativePath)"
478-
Condition="'%(ResolvedFileToPublish.RelativePath)' != 'web.config' AND !$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))" />
489+
RelativePath="$(_BlazorPublishOutputPath)$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('/','\').Substring(8))"
490+
Condition="'$(StaticWebAssetBasePath)' != '/' AND $([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('/','\').StartsWith('wwwroot\'))" />
479491

480-
<!-- Remove pdbs from the publish output -->
481-
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition="'$(CopyOutputSymbolsToPublishDirectory)' != 'true' AND '%(Extension)' == '.pdb'" />
492+
<!--
493+
Change all remaining publish output to publish to appear under the $(PublishDir)wwwroot\$(StaticWebAssetBasePath) path.
494+
-->
495+
<ResolvedFileToPublish
496+
RelativePath="$(_BlazorFrameworkPublishPath)%(ResolvedFileToPublish.RelativePath)"
497+
Condition="'%(ResolvedFileToPublish.RelativePath)' != 'web.config' AND !$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('/','\').StartsWith('wwwroot\'))" />
482498
</ItemGroup>
483499

484500
<ItemGroup Condition="'@(ResolvedFileToPublish->AnyHaveMetadataValue('RelativePath', 'web.config'))' != 'true'">
@@ -515,11 +531,11 @@ Copyright (c) .NET Foundation. All rights reserved.
515531
<ItemGroup>
516532
<ResolvedFileToPublish
517533
Include="$(IntermediateOutputPath)blazor.publish.boot.json"
518-
RelativePath="$(_BlazorOutputPath)blazor.boot.json" />
534+
RelativePath="$(_BlazorFrameworkPublishPath)blazor.boot.json" />
519535

520536
<ResolvedFileToPublish
521537
Include="@(_BlazorJSFile)"
522-
RelativePath="$(_BlazorOutputPath)%(FileName)%(Extension)" />
538+
RelativePath="$(_BlazorFrameworkPublishPath)%(FileName)%(Extension)" />
523539
</ItemGroup>
524540
</Target>
525541

src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ Copyright (c) .NET Foundation. All rights reserved.
128128
<ContentSourcePath Condition="'%(ServiceWorker.PublishedContent)' != ''">%(ServiceWorker.PublishedContent)</ContentSourcePath>
129129
<ContentSourcePath Condition="'%(ServiceWorker.PublishedContent)' == ''">%(ServiceWorker.Identity)</ContentSourcePath>
130130
<RelativePath>%(ServiceWorker.Identity)</RelativePath>
131+
<RelativePath Condition="$([System.String]::Copy('%(ServiceWorker.Identity)').Replace('/','\').StartsWith('wwwroot\'))">$(_BlazorPublishOutputPath)$([System.String]::Copy('%(ServiceWorker.Identity)').Substring(8))</RelativePath>
131132
</_ServiceWorkerIntermediatePublishFile>
132133

133134
<_ServiceWorkerPublishFile Include="@(ResolvedFileToPublish)" Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))">
@@ -161,7 +162,7 @@ Copyright (c) .NET Foundation. All rights reserved.
161162
<ResolvedFileToPublish
162163
Include="$(_ServiceWorkerAssetsManifestPublishIntermediateOutputPath)"
163164
CopyToPublishDirectory="PreserveNewest"
164-
RelativePath="wwwroot\$(ServiceWorkerAssetsManifest)"
165+
RelativePath="$(_BlazorPublishOutputPath)$(ServiceWorkerAssetsManifest)"
165166
ExcludeFromSingleFile="true" />
166167
</ItemGroup>
167168
</Target>

0 commit comments

Comments
 (0)