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 08a0bffb9073..92b52ae0da22 100644
--- a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets
+++ b/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets
@@ -84,8 +84,6 @@ Copyright (c) .NET Foundation. All rights reserved.
-
-
diff --git a/src/BlazorWasmSdk/Tasks/AssetsManifestFile.cs b/src/BlazorWasmSdk/Tasks/AssetsManifestFile.cs
index aed2f0f44be1..e8686acba161 100644
--- a/src/BlazorWasmSdk/Tasks/AssetsManifestFile.cs
+++ b/src/BlazorWasmSdk/Tasks/AssetsManifestFile.cs
@@ -4,6 +4,7 @@
namespace Microsoft.NET.Sdk.BlazorWebAssembly
{
#pragma warning disable IDE1006 // Naming Styles
+ // Only used in legacy builds (5.0 and earlier)
public class AssetsManifestFile
{
///
diff --git a/src/BlazorWasmSdk/Tasks/GenerateServiceWorkerAssetsManifest.cs b/src/BlazorWasmSdk/Tasks/GenerateServiceWorkerAssetsManifest.cs
index d3c39c0b17da..0227f239ca9c 100644
--- a/src/BlazorWasmSdk/Tasks/GenerateServiceWorkerAssetsManifest.cs
+++ b/src/BlazorWasmSdk/Tasks/GenerateServiceWorkerAssetsManifest.cs
@@ -7,6 +7,7 @@
namespace Microsoft.NET.Sdk.BlazorWebAssembly
{
+ // Only used in legacy builds (5.0 and earlier)
public partial class GenerateServiceWorkerAssetsManifest : Task
{
[Required]
diff --git a/src/RazorSdk/Razor.slnf b/src/RazorSdk/Razor.slnf
index c29dba3946f8..45c9fb6d2e58 100644
--- a/src/RazorSdk/Razor.slnf
+++ b/src/RazorSdk/Razor.slnf
@@ -10,11 +10,12 @@
"src\\RazorSdk\\Tool\\Microsoft.NET.Sdk.Razor.Tool.csproj",
"src\\Resolvers\\Microsoft.DotNet.NativeWrapper\\Microsoft.DotNet.NativeWrapper.csproj",
"src\\StaticWebAssetsSdk\\Tasks\\Microsoft.NET.Sdk.StaticWebAssets.Tasks.csproj",
+ "src\\WasmSdk\\Tasks\\Microsoft.NET.Sdk.WebAssembly.Tasks.csproj",
+ "test\\Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests\\Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests.csproj",
"test\\Microsoft.NET.Sdk.BlazorWebAssembly.Tests\\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj",
"test\\Microsoft.NET.Sdk.Razor.Tests\\Microsoft.NET.Sdk.Razor.Tests.csproj",
"test\\Microsoft.NET.Sdk.Razor.Tool.Tests\\Microsoft.NET.Sdk.Razor.Tool.Tests.csproj",
- "test\\Microsoft.NET.TestFramework\\Microsoft.NET.TestFramework.csproj",
- "src\\WasmSdk\\Tasks\\Microsoft.NET.Sdk.WebAssembly.Tasks.csproj"
+ "test\\Microsoft.NET.TestFramework\\Microsoft.NET.TestFramework.csproj"
]
}
}
\ No newline at end of file
diff --git a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ServiceWorkerAssetsManifest.targets
similarity index 98%
rename from src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets
rename to src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ServiceWorkerAssetsManifest.targets
index 369a14a5307b..3c1995ed3a54 100644
--- a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets
+++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ServiceWorkerAssetsManifest.targets
@@ -11,7 +11,11 @@ Copyright (c) .NET Foundation. All rights reserved.
-->
-
+
+
$(ResolveStaticWebAssetsInputsDependsOn);_AddServiceWorkerAssets
diff --git a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets
index dcf4a1e61781..9fceacee75fe 100644
--- a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets
+++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets
@@ -550,4 +550,6 @@ Copyright (c) .NET Foundation. All rights reserved.
+
+
diff --git a/src/StaticWebAssetsSdk/Tasks/ServiceWorker/AssetsManifestFile.cs b/src/StaticWebAssetsSdk/Tasks/ServiceWorker/AssetsManifestFile.cs
new file mode 100644
index 000000000000..9f3872a8c177
--- /dev/null
+++ b/src/StaticWebAssetsSdk/Tasks/ServiceWorker/AssetsManifestFile.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.StaticWebAssets.Tasks
+{
+#pragma warning disable IDE1006 // Naming Styles
+ public class AssetsManifestFile
+ {
+ ///
+ /// Gets or sets a version string.
+ ///
+ public string version { get; set; }
+
+ ///
+ /// Gets or sets the assets. Keys are URLs; values are base-64-formatted SHA256 content hashes.
+ ///
+ public AssetsManifestFileEntry[] assets { get; set; }
+ }
+
+ public class AssetsManifestFileEntry
+ {
+ ///
+ /// Gets or sets the asset URL. Normally this will be relative to the application's base href.
+ ///
+ public string url { get; set; }
+
+ ///
+ /// Gets or sets the file content hash. This should be the base-64-formatted SHA256 value.
+ ///
+ public string hash { get; set; }
+ }
+#pragma warning restore IDE1006 // Naming Styles
+}
diff --git a/src/StaticWebAssetsSdk/Tasks/ServiceWorker/GenerateServiceWorkerAssetsManifest.cs b/src/StaticWebAssetsSdk/Tasks/ServiceWorker/GenerateServiceWorkerAssetsManifest.cs
new file mode 100644
index 000000000000..2b99ea83c3a4
--- /dev/null
+++ b/src/StaticWebAssetsSdk/Tasks/ServiceWorker/GenerateServiceWorkerAssetsManifest.cs
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.Serialization.Json;
+using System.Security.Cryptography;
+using Microsoft.Build.Framework;
+
+namespace Microsoft.AspNetCore.StaticWebAssets.Tasks
+{
+ public partial class GenerateServiceWorkerAssetsManifest : Task
+ {
+ [Required]
+ public ITaskItem[] Assets { get; set; }
+
+ public string Version { get; set; }
+
+ [Required]
+ public string OutputPath { get; set; }
+
+ [Output]
+ public string CalculatedVersion { get; set; }
+
+ public override bool Execute()
+ {
+ using var fileStream = File.Create(OutputPath);
+ CalculatedVersion = GenerateAssetManifest(fileStream);
+
+ return true;
+ }
+
+ internal string GenerateAssetManifest(Stream stream)
+ {
+ var assets = new AssetsManifestFileEntry[Assets.Length];
+ Parallel.For(0, assets.Length, i =>
+ {
+ var item = Assets[i];
+ var hash = item.GetMetadata("FileHash");
+ var url = item.GetMetadata("AssetUrl");
+
+ if (string.IsNullOrEmpty(hash))
+ {
+ // Some files that are part of the service worker manifest may not have their hashes previously
+ // calcualted. Calculate them at this time.
+ using var sha = SHA256.Create();
+ using var file = File.OpenRead(item.ItemSpec);
+ var bytes = sha.ComputeHash(file);
+
+ hash = Convert.ToBase64String(bytes);
+ }
+
+ assets[i] = new AssetsManifestFileEntry
+ {
+ hash = "sha256-" + hash,
+ url = url,
+ };
+ });
+
+ var version = Version;
+ if (string.IsNullOrEmpty(version))
+ {
+ // If a version isn't specified (which is likely the most common case), construct a Version by combining
+ // the file names + hashes of all the inputs.
+
+ var combinedHash = string.Join(
+ Environment.NewLine,
+ assets.OrderBy(f => f.url, StringComparer.Ordinal).Select(f => f.hash));
+
+ using var sha = SHA256.Create();
+ var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(combinedHash));
+ version = Convert.ToBase64String(bytes).Substring(0, 8);
+ }
+
+ var data = new AssetsManifestFile
+ {
+ version = version,
+ assets = assets,
+ };
+
+ using var streamWriter = new StreamWriter(stream, Encoding.UTF8, bufferSize: 50, leaveOpen: true);
+ streamWriter.Write("self.assetsManifest = ");
+ streamWriter.Flush();
+
+ using var jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, ownsStream: false, indent: true);
+ new DataContractJsonSerializer(typeof(AssetsManifestFile)).WriteObject(jsonWriter, data);
+ jsonWriter.Flush();
+
+ streamWriter.WriteLine(";");
+
+ return version;
+ }
+ }
+}
diff --git a/test/Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests/Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests.csproj b/test/Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests/Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests.csproj
index 5fd78d717a58..386f91daa2a7 100644
--- a/test/Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests/Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests.csproj
+++ b/test/Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests/Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests.csproj
@@ -46,6 +46,7 @@
+
diff --git a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/ServiceWorkerAssert.cs b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/ServiceWorkerAssert.cs
index bafa5e939f22..04c838fad66e 100644
--- a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/ServiceWorkerAssert.cs
+++ b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/ServiceWorkerAssert.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text.Json;
+using Microsoft.AspNetCore.StaticWebAssets.Tasks;
namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tests
{
diff --git a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPwaManifestTests.cs b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPwaManifestTests.cs
index f0eb2148c85c..e24bb1db272d 100644
--- a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPwaManifestTests.cs
+++ b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPwaManifestTests.cs
@@ -18,11 +18,21 @@ public void Build_ServiceWorkerAssetsManifest_Works()
// Arrange
var expectedExtensions = new[] { ".pdb", ".js", ".wasm" };
var testAppName = "BlazorWasmWithLibrary";
- var testInstance = CreateAspNetSdkTestAsset(testAppName);
+ var testInstance = CreateAspNetSdkTestAsset(testAppName)
+ .WithProjectChanges((p, doc) =>
+ {
+ if (Path.GetFileName(p) == "blazorwasm.csproj")
+ {
+ var itemGroup = new XElement("PropertyGroup");
+ var serviceWorkerAssetsManifest = new XElement("ServiceWorkerAssetsManifest", "service-worker-assets.js");
+ itemGroup.Add(serviceWorkerAssetsManifest);
+ doc.Root.Add(itemGroup);
+ }
+ });
var buildCommand = new BuildCommand(testInstance, "blazorwasm");
buildCommand.WithWorkingDirectory(testInstance.TestRoot);
- buildCommand.Execute("/p:ServiceWorkerAssetsManifest=service-worker-assets.js")
+ buildCommand.Execute()
.Should().Pass();
var buildOutputDirectory = buildCommand.GetOutputDirectory(DefaultTfm).ToString();
@@ -136,10 +146,20 @@ public void Publish_UpdatesServiceWorkerVersionHash_WhenSourcesChange()
{
// Arrange
var testAppName = "BlazorWasmWithLibrary";
- var testInstance = CreateAspNetSdkTestAsset(testAppName);
+ var testInstance = CreateAspNetSdkTestAsset(testAppName)
+ .WithProjectChanges((p, doc) =>
+ {
+ if (Path.GetFileName(p) == "blazorwasm.csproj")
+ {
+ var itemGroup = new XElement("PropertyGroup");
+ var serviceWorkerAssetsManifest = new XElement("ServiceWorkerAssetsManifest", "service-worker-assets.js");
+ itemGroup.Add(serviceWorkerAssetsManifest);
+ doc.Root.Add(itemGroup);
+ }
+ });
var publishCommand = new PublishCommand(testInstance, "blazorwasm");
- publishCommand.Execute("/p:ServiceWorkerAssetsManifest=service-worker-assets.js").Should().Pass();
+ publishCommand.Execute().Should().Pass();
var publishOutputDirectory = publishCommand.GetOutputDirectory(DefaultTfm).ToString();
@@ -158,7 +178,7 @@ public void Publish_UpdatesServiceWorkerVersionHash_WhenSourcesChange()
// Assert
publishCommand = new PublishCommand(testInstance, "blazorwasm");
- publishCommand.Execute("/p:ServiceWorkerAssetsManifest=service-worker-assets.js").Should().Pass();
+ publishCommand.Execute().Should().Pass();
var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last();
var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/");
@@ -176,10 +196,20 @@ public void Publish_DeterministicAcrossBuilds_WhenNoSourcesChange()
{
// Arrange
var testAppName = "BlazorWasmWithLibrary";
- var testInstance = CreateAspNetSdkTestAsset(testAppName);
+ var testInstance = CreateAspNetSdkTestAsset(testAppName)
+ .WithProjectChanges((p, doc) =>
+ {
+ if (Path.GetFileName(p) == "blazorwasm.csproj")
+ {
+ var itemGroup = new XElement("PropertyGroup");
+ var serviceWorkerAssetsManifest = new XElement("ServiceWorkerAssetsManifest", "service-worker-assets.js");
+ itemGroup.Add(serviceWorkerAssetsManifest);
+ doc.Root.Add(itemGroup);
+ }
+ });
var publishCommand = new PublishCommand(testInstance, "blazorwasm");
- publishCommand.Execute("/p:ServiceWorkerAssetsManifest=service-worker-assets.js").Should().Pass();
+ publishCommand.Execute().Should().Pass();
var publishOutputDirectory = publishCommand.GetOutputDirectory(DefaultTfm).ToString();
@@ -194,7 +224,7 @@ public void Publish_DeterministicAcrossBuilds_WhenNoSourcesChange()
// Act && Assert
publishCommand = new PublishCommand(Log, Path.Combine(testInstance.TestRoot, "blazorwasm"));
- publishCommand.Execute("/p:ServiceWorkerAssetsManifest=service-worker-assets.js").Should().Pass();
+ publishCommand.Execute().Should().Pass();
var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last();
var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/");