diff --git a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets b/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets index f632de9957f9..16716864ec7b 100644 --- a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets +++ b/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets @@ -49,6 +49,7 @@ Copyright (c) .NET Foundation. All rights reserved. false false true + true <_TargetingNETBefore80>$([MSBuild]::VersionLessThan('$(TargetFrameworkVersion)', '8.0')) <_TargetingNET80OrLater>$([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '8.0')) @@ -83,6 +84,15 @@ Copyright (c) .NET Foundation. All rights reserved. GenerateBuildBlazorBootExtensionJson; + + $(ResolvePublishRelatedStaticWebAssetsDependsOn); + _ReplaceFingerprintedBlazorJsForPublish + + + $(ResolveCompressedFilesForPublishDependsOn); + _ReplaceFingerprintedBlazorJsForPublish + + $(GeneratePublishWasmBootJsonDependsOn); GeneratePublishBlazorBootExtensionJson; @@ -104,6 +114,7 @@ Copyright (c) .NET Foundation. All rights reserved. <_BlazorJsFile> _framework/%(Filename)%(Extension) + <_BlazorJSFingerprintPattern Include="Js" Pattern="*.js" Expression="#[.{fingerprint}]!" /> + + + <_BlazorJSStaticWebAsset Include="@(StaticWebAsset)" Condition="'%(FileName)' == '%(_BlazorJSFile.FileName)'" /> + <_BlazorJSPublishCandidate Include="%(_BlazorJSStaticWebAsset.RelativeDir)%(_BlazorJSStaticWebAsset.FileName).%(_BlazorJSStaticWebAsset.Fingerprint)%(_BlazorJSStaticWebAsset.Extension)" /> + <_BlazorJSPublishCandidate> + _framework/$([System.IO.Path]::GetFileNameWithoutExtension('%(Filename)'))%(Extension) + + + + + + + + + + + <_BlazorJSStaticWebAssetFullPath>@(_BlazorJSStaticWebAsset->'%(FullPath)') + + + <_BlazorJSStaticWebAsset Include="@(StaticWebAsset)" Condition="'%(AssetTraitName)' == 'Content-Encoding' and '%(RelatedAsset)' == '$(_BlazorJSStaticWebAssetFullPath)'" /> + + + + + + + + + + + + diff --git a/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsFingerprintingTest.cs b/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsFingerprintingTest.cs index 10e90ba8d34b..85612bd71224 100644 --- a/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsFingerprintingTest.cs +++ b/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsFingerprintingTest.cs @@ -46,17 +46,18 @@ public void Build_FingerprintsContent_WhenEnabled() AssertBuildAssets(manifest1, outputPath, intermediateOutputPath); } - public static TheoryData WriteImportMapToHtmlData => new TheoryData + public static TheoryData WriteImportMapToHtmlData => new TheoryData { - { "VanillaWasm", true }, - { "BlazorWasmMinimal", false } + { "VanillaWasm", "main.js", null }, + { "BlazorWasmMinimal", "_framework/blazor.webassembly.js", "_framework/blazor.webassembly#[.{fingerprint}].js" } }; [Theory] [MemberData(nameof(WriteImportMapToHtmlData))] - public void Build_WriteImportMapToHtml(string testAsset, bool assetMainJs) + public void Build_WriteImportMapToHtml(string testAsset, string scriptPath, string scriptPathWithFingerprintPattern) { ProjectDirectory = CreateAspNetSdkTestAsset(testAsset); + ReplaceStringInIndexHtml(ProjectDirectory, scriptPath, scriptPathWithFingerprintPattern); var build = CreateBuildCommand(ProjectDirectory); ExecuteCommand(build, "-p:WriteImportMapToHtml=true").Should().Pass(); @@ -65,14 +66,15 @@ public void Build_WriteImportMapToHtml(string testAsset, bool assetMainJs) var indexHtmlPath = Directory.EnumerateFiles(Path.Combine(intermediateOutputPath, "staticwebassets", "importmaphtml", "build"), "*.html").Single(); var endpointsManifestPath = Path.Combine(intermediateOutputPath, $"staticwebassets.build.endpoints.json"); - AssertImportMapInHtml(indexHtmlPath, endpointsManifestPath, assetMainJs); + AssertImportMapInHtml(indexHtmlPath, endpointsManifestPath, scriptPath); } [Theory] [MemberData(nameof(WriteImportMapToHtmlData))] - public void Publish_WriteImportMapToHtml(string testAsset, bool assetMainJs) + public void Publish_WriteImportMapToHtml(string testAsset, string scriptPath, string scriptPathWithFingerprintPattern) { ProjectDirectory = CreateAspNetSdkTestAsset(testAsset); + ReplaceStringInIndexHtml(ProjectDirectory, scriptPath, scriptPathWithFingerprintPattern); var projectName = Path.GetFileNameWithoutExtension(Directory.EnumerateFiles(ProjectDirectory.TestRoot, "*.csproj").Single()); @@ -80,29 +82,40 @@ public void Publish_WriteImportMapToHtml(string testAsset, bool assetMainJs) ExecuteCommand(publish, "-p:WriteImportMapToHtml=true").Should().Pass(); var outputPath = publish.GetOutputDirectory(DefaultTfm, "Debug").ToString(); - var indexHtmlPath = Path.Combine(outputPath, "wwwroot", "index.html"); + var indexHtmlOutputPath = Path.Combine(outputPath, "wwwroot", "index.html"); var endpointsManifestPath = Path.Combine(outputPath, $"{projectName}.staticwebassets.endpoints.json"); - AssertImportMapInHtml(indexHtmlPath, endpointsManifestPath, assetMainJs); + AssertImportMapInHtml(indexHtmlOutputPath, endpointsManifestPath, scriptPath); } - private void AssertImportMapInHtml(string indexHtmlPath, string endpointsManifestPath, bool assetMainJs) + private void ReplaceStringInIndexHtml(TestAsset testAsset, string scriptPath, string scriptPathWithFingerprintPattern) + { + if (scriptPathWithFingerprintPattern != null) + { + var indexHtmlPath = Path.Combine(testAsset.TestRoot, "wwwroot", "index.html"); + var indexHtmlContent = File.ReadAllText(indexHtmlPath); + var newIndexHtmlContent = indexHtmlContent.Replace(scriptPath, scriptPathWithFingerprintPattern); + if (indexHtmlContent == newIndexHtmlContent) + throw new Exception($"Script replacement '{scriptPath}' for '{scriptPathWithFingerprintPattern}' didn't produce any change in '{indexHtmlPath}'"); + + File.WriteAllText(indexHtmlPath, newIndexHtmlContent); + } + } + + private void AssertImportMapInHtml(string indexHtmlPath, string endpointsManifestPath, string scriptPath) { var indexHtmlContent = File.ReadAllText(indexHtmlPath); var endpoints = JsonSerializer.Deserialize(File.ReadAllText(endpointsManifestPath)); - if (assetMainJs) - { - var mainJs = GetFingerprintedPath("main.js"); - Assert.DoesNotContain("src=\"main.js\"", indexHtmlContent); - Assert.Contains($"src=\"{mainJs}\"", indexHtmlContent); - } + var fingerprintedScriptPath = GetFingerprintedPath(scriptPath); + Assert.DoesNotContain($"src=\"{scriptPath}\"", indexHtmlContent); + Assert.Contains($"src=\"{fingerprintedScriptPath}\"", indexHtmlContent); Assert.Contains(GetFingerprintedPath("_framework/dotnet.js"), indexHtmlContent); Assert.Contains(GetFingerprintedPath("_framework/dotnet.native.js"), indexHtmlContent); Assert.Contains(GetFingerprintedPath("_framework/dotnet.runtime.js"), indexHtmlContent); string GetFingerprintedPath(string route) - => endpoints.Endpoints.FirstOrDefault(e => e.Route == route && e.Selectors.Length == 0)?.AssetFile ?? throw new Exception($"Missing endpoint for file '{route}'"); + => endpoints.Endpoints.FirstOrDefault(e => e.Route == route && e.Selectors.Length == 0)?.AssetFile ?? throw new Exception($"Missing endpoint for file '{route}' in '{endpointsManifestPath}'"); } }