diff --git a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.HtmlImportMap.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.HtmlAssetPlaceholders.targets
similarity index 64%
rename from src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.HtmlImportMap.targets
rename to src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.HtmlAssetPlaceholders.targets
index 579338938557..dee61365fd50 100644
--- a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.HtmlImportMap.targets
+++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.HtmlAssetPlaceholders.targets
@@ -1,6 +1,6 @@
+ true
+
$(ResolveBuildRelatedStaticWebAssetsDependsOn);
- ResolveHtmlImportMapBuildStaticWebAssets;
+ ResolveHtmlAssetPlaceholdersBuildStaticWebAssets;
$(ResolveCompressedFilesDependsOn);
- ResolveHtmlImportMapBuildStaticWebAssets
+ ResolveHtmlAssetPlaceholdersBuildStaticWebAssets
$(ResolveBuildServiceWorkerStaticWebAssetsDependsOn);
- ResolveHtmlImportMapBuildStaticWebAssets
+ ResolveHtmlAssetPlaceholdersBuildStaticWebAssets
-
- GenerateHtmlImportMapBuildStaticWebAssets;
- $(ResolveHtmlImportMapBuildStaticWebAssetsDependsOn)
-
-
- ResolveHtmlImportMapBuildConfiguration;
- $(GenerateHtmlImportMapBuildStaticWebAssetsDependsOn)
-
+
+ $(ResolveHtmlAssetPlaceholdersBuildStaticWebAssetsDependsOn);
+ GenerateHtmlAssetPlaceholdersBuildStaticWebAssets
+
+
+ $(GenerateHtmlAssetPlaceholdersBuildStaticWebAssetsDependsOn);
+ ResolveHtmlAssetPlaceholdersBuildConfiguration
+
$(ResolvePublishRelatedStaticWebAssetsDependsOn);
- ResolveHtmlImportMapPublishStaticWebAssets
+ ResolveHtmlAssetPlaceholdersPublishStaticWebAssets
$(ResolvePublishCompressedStaticWebAssetsDependsOn);
- ResolveHtmlImportMapPublishStaticWebAssets
+ ResolveHtmlAssetPlaceholdersPublishStaticWebAssets
$(ResolvePublishServiceWorkerStaticWebAssetsDependsOn);
- ResolveHtmlImportMapPublishStaticWebAssets
+ ResolveHtmlAssetPlaceholdersPublishStaticWebAssets
-
- GenerateHtmlImportMapPublishStaticWebAssets;
- $(ResolveHtmlImportMapPublishStaticWebAssetsDependsOn)
-
-
- ResolveHtmlImportMapPublishConfiguration;
- $(GenerateHtmlImportMapPublishStaticWebAssetsDependsOn)
-
+
+ $(ResolveHtmlAssetPlaceholdersPublishStaticWebAssetsDependsOn);
+ GenerateHtmlAssetPlaceholdersPublishStaticWebAssets
+
+
+ $(GenerateHtmlAssetPlaceholdersPublishStaticWebAssetsDependsOn);
+ ResolveHtmlAssetPlaceholdersPublishConfiguration
+
-
+
- <_BuildImportMapHtmlPath>$([MSBuild]::NormalizeDirectory($(_StaticWebAssetsIntermediateOutputPath), 'importmaphtml', 'build'))
+ <_BuildHtmlAssetPlaceholdersPath>$([MSBuild]::NormalizeDirectory($(_StaticWebAssetsIntermediateOutputPath), 'htmlassetplaceholders', 'build'))
-
+
<_HtmlStaticWebAssets Include="@(StaticWebAsset)" Condition="'%(AssetKind)' != 'Publish' and '%(Extension)' == '.html'" />
@@ -95,25 +100,25 @@ Copyright (c) .NET Foundation. All rights reserved.
-
-
+
+ OutputPath="$(_BuildHtmlAssetPlaceholdersPath)">
-
+
-
+
<_HtmlCandidatesNoMetadata
Include="@(_HtmlCandidates)"
RemoveMetadata="SourceType;AssetKind;Integrity;Fingerprint" />
- <_HtmlCandidatesNoMetadata ContentRoot="$(_BuildImportMapHtmlPath)" />
+ <_HtmlCandidatesNoMetadata ContentRoot="$(_BuildHtmlAssetPlaceholdersPath)" />
-
+
- <_PublishImportMapHtmlPath>$([MSBuild]::NormalizeDirectory($(_StaticWebAssetsIntermediateOutputPath), 'importmaphtml', 'publish'))
+ <_PublishHtmlAssetPlaceholdersPath>$([MSBuild]::NormalizeDirectory($(_StaticWebAssetsIntermediateOutputPath), 'htmlassetplaceholders', 'publish'))
-
+
<_EsModuleCandidateForPublish Include="@(StaticWebAsset)" Condition="'%(AssetKind)' != 'Build'" />
@@ -165,25 +170,25 @@ Copyright (c) .NET Foundation. All rights reserved.
-
-
+
+ OutputPath="$(_PublishHtmlAssetPlaceholdersPath)">
-
+
-
+
<_HtmlPublishCandidatesNoMetadata
Include="@(_HtmlPublishCandidates)"
RemoveMetadata="SourceType;AssetKind;Integrity;Fingerprint" />
- <_HtmlPublishCandidatesNoMetadata ContentRoot="$(_PublishImportMapHtmlPath)" />
+ <_HtmlPublishCandidatesNoMetadata ContentRoot="$(_PublishHtmlAssetPlaceholdersPath)" />
-
+
diff --git a/src/StaticWebAssetsSdk/Tasks/WriteImportMapToHtml.cs b/src/StaticWebAssetsSdk/Tasks/OverrideHtmlAssetPlaceholders.cs
similarity index 62%
rename from src/StaticWebAssetsSdk/Tasks/WriteImportMapToHtml.cs
rename to src/StaticWebAssetsSdk/Tasks/OverrideHtmlAssetPlaceholders.cs
index a7478f89018c..e69a194c6407 100644
--- a/src/StaticWebAssetsSdk/Tasks/WriteImportMapToHtml.cs
+++ b/src/StaticWebAssetsSdk/Tasks/OverrideHtmlAssetPlaceholders.cs
@@ -1,17 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Text.RegularExpressions;
+using Microsoft.AspNetCore.StaticWebAssets.Tasks.Utils;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Text.Encodings.Web;
using System.Text.Json.Serialization;
using System.Text.Json;
-using Microsoft.AspNetCore.StaticWebAssets.Tasks.Utils;
+using System.Text.RegularExpressions;
namespace Microsoft.AspNetCore.StaticWebAssets.Tasks;
-public partial class WriteImportMapToHtml : Task
+public partial class OverrideHtmlAssetPlaceholders : Task
{
[Required]
public ITaskItem[] Assets { get; set; } = [];
@@ -37,14 +37,11 @@ public partial class WriteImportMapToHtml : Task
[Output]
public string[] FileWrites { get; set; } = [];
- // "([^"]+)(#\[\.{fingerprint}\])([^"]+)"
- // 1.group = file name
- // 2.group = fingerprint placeholder
- // 3.group = file extension
- // wrapped in quotes
- private static readonly Regex _assetsRegex = new Regex(@"""([^""]+)(#\[\.{fingerprint}\])([^""]+)""");
+ internal static readonly Regex _assetsRegex = new Regex(@"""(?[^""]+)#\[\.{fingerprint}\](?[^""]+)""");
+
+ internal static readonly Regex _importMapRegex = new Regex(@"");
- private static readonly Regex _importMapRegex = new Regex(@"");
+ internal static readonly Regex _preloadRegex = new Regex(@"[^""]+)"")?\s*[/]?>");
public override bool Execute()
{
@@ -75,10 +72,17 @@ public override bool Execute()
return $"";
});
+ // Generate import map
+ outputContent = _preloadRegex.Replace(outputContent, e =>
+ {
+ Log.LogMessage("Writing preload links to '{0}'", item.ItemSpec);
+ return GeneratePreloadLinks(resources, e.Groups["group"]?.Value);
+ });
+
// Fingerprint all assets used in html
outputContent = _assetsRegex.Replace(outputContent, e =>
{
- string assetPath = e.Groups[1].Value + e.Groups[3].Value;
+ string assetPath = e.Groups["fileName"].Value + e.Groups["fileExtension"].Value;
string fingerprintedAssetPath = GetFingerprintedAssetPath(urlMappings, assetPath);
Log.LogMessage("Replacing asset '{0}' with fingerprinted version '{1}'", assetPath, fingerprintedAssetPath);
return "\"" + fingerprintedAssetPath + "\"";
@@ -105,6 +109,48 @@ public override bool Execute()
return true;
}
+ private static string GeneratePreloadLinks(List assets, string? group)
+ {
+ var links = new List<(int Order, string Value)>();
+ foreach (var asset in assets)
+ {
+ if (asset.PreloadRel == null)
+ {
+ continue;
+ }
+
+ if (group != null && asset.PreloadGroup != group)
+ {
+ continue;
+ }
+
+ var link = new StringBuilder();
+ link.Append($"");
+ links.Add((asset.PreloadOrder, link.ToString()));
+ }
+
+ links.Sort((a, b) => a.Order.CompareTo(b.Order));
+ return String.Join(Environment.NewLine, links.Select(l => l.Value));
+ }
+
private string GetFingerprintedAssetPath(Dictionary urlMappings, string assetPath)
{
if (urlMappings.TryGetValue(assetPath, out var asset) && (!IncludeOnlyHardFingerprintedModules || asset.IsHardFingerprinted))
@@ -125,33 +171,59 @@ internal List CreateResourcesFromEndpoints(IEnumerable a.ItemSpec == endpoint.AssetFile);
if (asset != null)
{
- isHardFingerprinted = asset.GetMetadata("RelativePath").Contains("#[.{fingerprint}]!");
+ resourceAsset.IsHardFingerprinted = asset.GetMetadata("RelativePath").Contains("#[.{fingerprint}]!");
}
- resources.Add(new ResourceAsset(endpoint.Route, label, integrity, isHardFingerprinted));
+ resources.Add(resourceAsset);
}
}
@@ -207,12 +279,18 @@ private static Dictionary GroupResourcesByLabel(List imports, Dictionary> scopes, Dictionary integrity)
diff --git a/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs b/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs
new file mode 100644
index 000000000000..31e709b85f12
--- /dev/null
+++ b/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs
@@ -0,0 +1,290 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable disable
+
+using Microsoft.AspNetCore.StaticWebAssets.Tasks;
+using System.Text.RegularExpressions;
+
+namespace Microsoft.AspNetCore.Razor.Tasks;
+
+public class OverrideHtmlAssetPlaceholdersTest
+{
+ [Theory]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "main.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "main.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "main.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "./main.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "./folder/folder/file.name.something.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "main.suffix.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "/root/main.suffix.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+ main#[.{fingerprint}].js
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "main.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "./main.js"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "main.js"
+ )]
+ public void ValidateAssetsRegex(string input, bool shouldMatch, string fileName = null)
+ {
+ var match = OverrideHtmlAssetPlaceholders._assetsRegex.Match(input);
+ Assert.Equal(shouldMatch, match.Success);
+
+ if (fileName != null)
+ {
+ Assert.Equal(fileName, match.Groups["fileName"].Value + match.Groups["fileExtension"].Value);
+ }
+ }
+
+ [Theory]
+ [InlineData(
+ """
+
+ """,
+ true
+ )]
+ [InlineData(
+ """
+
+ """,
+ true
+ )]
+ [InlineData(
+ """
+
+ """,
+ true
+ )]
+ [InlineData(
+ """
+
+ """,
+ true
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ public void ValidateImportMapRegex(string input, bool shouldMatch)
+ {
+ Assert.Equal(shouldMatch, OverrideHtmlAssetPlaceholders._importMapRegex.Match(input).Success);
+ }
+
+ [Theory]
+ [InlineData(
+ """
+
+ """,
+ true
+ )]
+ [InlineData(
+ """
+
+ """,
+ true
+ )]
+ [InlineData(
+ """
+
+ """,
+ true
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+ "
+ """,
+ false
+ )]
+ [InlineData(
+ """
+ "
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "webassembly"
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "webassembly"
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ false
+ )]
+ [InlineData(
+ """
+
+ """,
+ true,
+ "webassembly"
+ )]
+ public void ValidatePreloadRegex(string input, bool shouldMatch, string group = null)
+ {
+ var match = OverrideHtmlAssetPlaceholders._preloadRegex.Match(input);
+ Assert.Equal(shouldMatch, match.Success);
+
+ if (group != null)
+ {
+ Assert.Equal(group, match.Groups["group"]?.Value);
+ }
+ }
+}
diff --git a/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsFingerprintingTest.cs b/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsFingerprintingTest.cs
index feb337d3c2c2..15438446676a 100644
--- a/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsFingerprintingTest.cs
+++ b/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsFingerprintingTest.cs
@@ -46,7 +46,7 @@ public void Build_FingerprintsContent_WhenEnabled()
AssertBuildAssets(manifest1, outputPath, intermediateOutputPath);
}
- public static TheoryData WriteImportMapToHtmlData => new TheoryData
+ public static TheoryData OverrideHtmlAssetPlaceholdersData => new TheoryData
{
{ "VanillaWasm", "main.js", "main#[.{fingerprint}].js", true, true },
{ "VanillaWasm", "main.js", null, false, false },
@@ -54,41 +54,41 @@ public void Build_FingerprintsContent_WhenEnabled()
};
[Theory]
- [MemberData(nameof(WriteImportMapToHtmlData))]
- public void Build_WriteImportMapToHtml(string testAsset, string scriptPath, string scriptPathWithFingerprintPattern, bool fingerprintUserJavascriptAssets, bool expectFingerprintOnScript)
+ [MemberData(nameof(OverrideHtmlAssetPlaceholdersData))]
+ public void Build_OverrideHtmlAssetPlaceholders(string testAsset, string scriptPath, string scriptPathWithFingerprintPattern, bool fingerprintUserJavascriptAssets, bool expectFingerprintOnScript)
{
- ProjectDirectory = CreateAspNetSdkTestAsset(testAsset);
+ ProjectDirectory = CreateAspNetSdkTestAsset(testAsset, identifier: $"{testAsset}_{fingerprintUserJavascriptAssets}_{expectFingerprintOnScript}");
ReplaceStringInIndexHtml(ProjectDirectory, scriptPath, scriptPathWithFingerprintPattern);
FingerprintUserJavascriptAssets(fingerprintUserJavascriptAssets);
var build = CreateBuildCommand(ProjectDirectory);
- ExecuteCommand(build, "-p:WriteImportMapToHtml=true", $"-p:FingerprintUserJavascriptAssets={fingerprintUserJavascriptAssets}").Should().Pass();
+ ExecuteCommand(build, "-p:OverrideHtmlAssetPlaceholders=true", $"-p:FingerprintUserJavascriptAssets={fingerprintUserJavascriptAssets.ToString().ToLower()}").Should().Pass();
var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString();
- var indexHtmlPath = Directory.EnumerateFiles(Path.Combine(intermediateOutputPath, "staticwebassets", "importmaphtml", "build"), "*.html").Single();
+ var indexHtmlPath = Directory.EnumerateFiles(Path.Combine(intermediateOutputPath, "staticwebassets", "htmlassetplaceholders", "build"), "*.html").Single();
var endpointsManifestPath = Path.Combine(intermediateOutputPath, $"staticwebassets.build.endpoints.json");
- AssertImportMapInHtml(indexHtmlPath, endpointsManifestPath, scriptPath, expectFingerprintOnScript: expectFingerprintOnScript);
+ AssertImportMapInHtml(indexHtmlPath, endpointsManifestPath, scriptPath, expectFingerprintOnScript: expectFingerprintOnScript, expectPreloadElement: testAsset == "VanillaWasm");
}
[Theory]
- [MemberData(nameof(WriteImportMapToHtmlData))]
- public void Publish_WriteImportMapToHtml(string testAsset, string scriptPath, string scriptPathWithFingerprintPattern, bool fingerprintUserJavascriptAssets, bool expectFingerprintOnScript)
+ [MemberData(nameof(OverrideHtmlAssetPlaceholdersData))]
+ public void Publish_OverrideHtmlAssetPlaceholders(string testAsset, string scriptPath, string scriptPathWithFingerprintPattern, bool fingerprintUserJavascriptAssets, bool expectFingerprintOnScript)
{
- ProjectDirectory = CreateAspNetSdkTestAsset(testAsset);
+ ProjectDirectory = CreateAspNetSdkTestAsset(testAsset, identifier: $"{testAsset}_{fingerprintUserJavascriptAssets}_{expectFingerprintOnScript}");
ReplaceStringInIndexHtml(ProjectDirectory, scriptPath, scriptPathWithFingerprintPattern);
FingerprintUserJavascriptAssets(fingerprintUserJavascriptAssets);
var projectName = Path.GetFileNameWithoutExtension(Directory.EnumerateFiles(ProjectDirectory.TestRoot, "*.csproj").Single());
var publish = CreatePublishCommand(ProjectDirectory);
- ExecuteCommand(publish, "-p:WriteImportMapToHtml=true", $"-p:FingerprintUserJavascriptAssets={fingerprintUserJavascriptAssets}").Should().Pass();
+ ExecuteCommand(publish, "-p:OverrideHtmlAssetPlaceholders=true", $"-p:FingerprintUserJavascriptAssets={fingerprintUserJavascriptAssets.ToString().ToLower()}").Should().Pass();
var outputPath = publish.GetOutputDirectory(DefaultTfm, "Debug").ToString();
var indexHtmlOutputPath = Path.Combine(outputPath, "wwwroot", "index.html");
var endpointsManifestPath = Path.Combine(outputPath, $"{projectName}.staticwebassets.endpoints.json");
- AssertImportMapInHtml(indexHtmlOutputPath, endpointsManifestPath, scriptPath, expectFingerprintOnScript: expectFingerprintOnScript);
+ AssertImportMapInHtml(indexHtmlOutputPath, endpointsManifestPath, scriptPath, expectFingerprintOnScript: expectFingerprintOnScript, expectPreloadElement: testAsset == "VanillaWasm");
}
private void FingerprintUserJavascriptAssets(bool fingerprintUserJavascriptAssets)
@@ -125,7 +125,7 @@ private void ReplaceStringInIndexHtml(TestAsset testAsset, string sourceValue, s
}
}
- private void AssertImportMapInHtml(string indexHtmlPath, string endpointsManifestPath, string scriptPath, bool expectFingerprintOnScript = true)
+ private void AssertImportMapInHtml(string indexHtmlPath, string endpointsManifestPath, string scriptPath, bool expectFingerprintOnScript = true, bool expectPreloadElement = false)
{
var indexHtmlContent = File.ReadAllText(indexHtmlPath);
var endpoints = JsonSerializer.Deserialize(File.ReadAllText(endpointsManifestPath));
@@ -150,6 +150,12 @@ private void AssertImportMapInHtml(string indexHtmlPath, string endpointsManifes
Assert.Contains(GetFingerprintedPath("_framework/dotnet.native.js"), indexHtmlContent);
Assert.Contains(GetFingerprintedPath("_framework/dotnet.runtime.js"), indexHtmlContent);
+ if (expectPreloadElement)
+ {
+ Assert.DoesNotContain("", indexHtmlContent);
+ Assert.Contains($" endpoints.Endpoints.FirstOrDefault(e => e.Route == route && e.Selectors.Length == 0)?.AssetFile ?? throw new Exception($"Missing endpoint for file '{route}' in '{endpointsManifestPath}'");
}
diff --git a/test/TestAssets/TestProjects/VanillaWasm/VanillaWasm.csproj b/test/TestAssets/TestProjects/VanillaWasm/VanillaWasm.csproj
index 9676d0bfc24d..eb60f30a679a 100644
--- a/test/TestAssets/TestProjects/VanillaWasm/VanillaWasm.csproj
+++ b/test/TestAssets/TestProjects/VanillaWasm/VanillaWasm.csproj
@@ -2,5 +2,63 @@
$(CurrentTargetFramework)
true
+
+ _AddAppPreloadProperties;
+ $(GenerateHtmlAssetPlaceholdersBuildStaticWebAssetsDependsOn)
+
+
+
+ <_AppendPreloadRelPreloadProperty Include="Append">
+ Property
+ PreloadRel
+ preload
+
+ <_AppendPreloadAsScriptProperty Include="Append">
+ Property
+ PreloadAs
+ script
+
+ <_AppendPreloadPriorityHighProperty Include="Append">
+ Property
+ PreloadPriority
+ high
+
+ <_AppendPreloadCrossoriginAnonymousProperty Include="Append">
+ Property
+ PreloadCrossorigin
+ anonymous
+
+ <_AppendPreloadGroupWebAssemblyProperty Include="Append">
+ Property
+ PreloadGroup
+ webassembly
+
+
+
+
+
+ <_AppPreloadScriptAsset Include="@(StaticWebAsset)" Condition="'%(FileName)%(Extension)' == 'main.js'" />
+ <_AppPreloadEndpointFilter Include="Property" Name="Label" Mode="Include" Condition="'$(FingerprintUserJavascriptAssets)' == 'true'" />
+ <_AppPreloadEndpointFilter Include="Property" Name="Label" Mode="Exclude" Condition="'@(_AppPreloadEndpointFilter)' == ''" />
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/TestAssets/TestProjects/VanillaWasm/wwwroot/index.html b/test/TestAssets/TestProjects/VanillaWasm/wwwroot/index.html
index 8a30a27eb74e..3193039a8b68 100644
--- a/test/TestAssets/TestProjects/VanillaWasm/wwwroot/index.html
+++ b/test/TestAssets/TestProjects/VanillaWasm/wwwroot/index.html
@@ -7,6 +7,7 @@
WasmMySdk
+