Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8b7d1ce
Use WriteImportMapToHtml=true in browser template
maraf Mar 18, 2025
e698556
Update WBT to expect fingerprint on boot config
maraf Mar 25, 2025
fce45dd
Merge branch 'main' into BrowserWriteImportMapToHtml
maraf Mar 25, 2025
514724a
Update WBT to expect fingerprint on dotnet.js
maraf Mar 25, 2025
d44b49d
Expect fingerprint on boot config
maraf Mar 26, 2025
4c99203
Fix missing return statement
maraf Mar 26, 2025
e45965d
Fix
maraf Mar 26, 2025
53781d2
Merge branch 'main' into BrowserWriteImportMapToHtml
maraf Mar 26, 2025
00455fe
Merge branch 'main' into BrowserWriteImportMapToHtml
maraf Apr 9, 2025
05e8bd7
Rename WriteImportMapToHtml to OverrideHtmlAssetPlaceholders
maraf Apr 9, 2025
47d6dbb
Merge remote-tracking branch 'upstream/main' into BrowserWriteImportM…
maraf Apr 17, 2025
b37702b
Merge remote-tracking branch 'upstream/main' into BrowserWriteImportM…
maraf Apr 17, 2025
95b80f4
Rename WriteImportMapToHtml to OverrideHtmlAssetPlaceholders
maraf Apr 17, 2025
95c4369
Use standard boot config reading methods
maraf Apr 17, 2025
4c710ed
Merge branch 'main' into BrowserWriteImportMapToHtml
maraf Apr 18, 2025
c6e8257
Merge branch 'main' into BrowserWriteImportMapToHtml
maraf Apr 19, 2025
694d6f6
Use S.T.J to parse boot config tests
maraf Apr 19, 2025
f0a6acf
Fix nullable in tests
maraf Apr 19, 2025
6e3176d
Include boot config content when parsing fails
maraf Apr 19, 2025
c1fd508
Try to exclude other dotnet files when looking for boot config
maraf Apr 19, 2025
3f6dcf0
Fix typo
maraf Apr 19, 2025
6add7e2
Merge branch 'main' into BrowserWriteImportMapToHtml
maraf Apr 19, 2025
f66a457
Fix dotnet.boot.js in tests
maraf Apr 19, 2025
5b87630
Fix custom boot config name ending with json and OverrideHtmlAssetPla…
maraf Apr 21, 2025
666b3f0
OverrideHtmlAssetPlaceholders=true explicitly in blazor. Expect dotne…
maraf Apr 21, 2025
d721378
Don't compare dotnet.js in compare stats
maraf Apr 21, 2025
442fee2
Merge branch 'main' into BrowserWriteImportMapToHtml
lewing Apr 21, 2025
90f6aec
Merge branch 'main' into BrowserWriteImportMapToHtml
maraf Apr 22, 2025
c5a4663
WasmRunOutOfAppBundleTests uses custom HTML
maraf Apr 22, 2025
702f80f
OverrideHtmlAssetPlaceholders in LibraryMode test
maraf Apr 22, 2025
480c330
Add preload placeholder to template
maraf Apr 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<!-- true = boot config will have hard fingerprint; false = boot config will have soft fingerprint -->
<_WasmFingerprintBootConfig Condition="'$(_WasmInlineBootConfig)' == 'true'">$(_WasmFingerprintDotnetJs)</_WasmFingerprintBootConfig>
<_WasmFingerprintBootConfig Condition="'$(_WasmFingerprintBootConfig)' == ''">$(WasmFingerprintBootConfig)</_WasmFingerprintBootConfig>
<_WasmFingerprintBootConfig Condition="'$(_WasmFingerprintBootConfig)' == ''and $(_WasmBootConfigFileName.EndsWith('.js'))">$(OverrideHtmlAssetPlaceholders)</_WasmFingerprintBootConfig>
<_WasmFingerprintBootConfig Condition="'$(_WasmFingerprintBootConfig)' == '' and $(_WasmBootConfigFileName.EndsWith('.js'))">$(OverrideHtmlAssetPlaceholders)</_WasmFingerprintBootConfig>
<_WasmFingerprintBootConfig Condition="'$(_WasmFingerprintBootConfig)' == ''">false</_WasmFingerprintBootConfig>
<_WasmPreloadAssets>$(WasmPreloadAssets)</_WasmPreloadAssets>
<_WasmPreloadAssets Condition="'$(_WasmPreloadAssets)' == ''">true</_WasmPreloadAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public AppsettingsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture
public async Task FileInVfs()
{
Configuration config = Configuration.Debug;
ProjectInfo info = CreateWasmTemplateProject(Template.BlazorWasm, config, aot: false, "blazor");
ProjectInfo info = CreateWasmTemplateProject(Template.BlazorWasm, config, aot: false, "blazor", extraProperties: "<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>");
UpdateHomePage();
string projectDirectory = Path.GetDirectoryName(info.ProjectFilePath)!;
File.WriteAllText(Path.Combine(projectDirectory, "wwwroot", "appsettings.json"), $"{{ \"Id\": \"{info.ProjectName}\" }}");
Expand Down
21 changes: 7 additions & 14 deletions src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using System.Text.Json;
using Microsoft.NET.Sdk.WebAssembly;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
Expand Down Expand Up @@ -84,23 +85,15 @@ public void BugRegression_60479_WithRazorClassLib()
ProjectInfo info = CopyTestAsset(config, aot: true, TestAsset.BlazorBasicTestApp, "blz_razor_lib_top", extraItems: extraItems);

// No relinking, no AOT
BlazorBuild(info, config);
BlazorBuild(info, config, new BuildOptions(UseCache: false));

// will relink
BlazorPublish(info, config, new PublishOptions(UseCache: false, BootConfigFileName: "blazor.boot.json"));
BlazorPublish(info, config, new PublishOptions(UseCache: false));

// publish/wwwroot/_framework/blazor.boot.json
string frameworkDir = GetBlazorBinFrameworkDir(config, forPublish: true);
string bootJson = Path.Combine(frameworkDir, "blazor.boot.json");

Assert.True(File.Exists(bootJson), $"Could not find {bootJson}");
var jdoc = JsonDocument.Parse(File.ReadAllText(bootJson));
if (!jdoc.RootElement.TryGetProperty("resources", out JsonElement resValue) ||
!resValue.TryGetProperty("lazyAssembly", out JsonElement lazyVal))
{
throw new XunitException($"Could not find resources.lazyAssembly object in {bootJson}");
}

Assert.True(lazyVal.EnumerateObject().Select(jp => jp.Name).FirstOrDefault(f => f.StartsWith(razorClassLibraryName)) != null);
string bootConfigPath = _provider.GetBootConfigPath(GetBlazorBinFrameworkDir(config, forPublish: true));
BootJsonData bootJson = _provider.GetBootJson(bootConfigPath);

Assert.Contains(bootJson.resources.lazyAssembly.Keys, f => f.StartsWith(razorClassLibraryName));
}
}
93 changes: 65 additions & 28 deletions src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,17 +366,13 @@ private string[] GetFilesMatchingNameConsideringFingerprinting(string filePath,
dict[Path.GetFileName(file)] = (file, unchanged);

// those files do not change on re-link
dict["dotnet.js"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.js"), false); // Inline boot config
dict["dotnet.js.map"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.js.map"), true);
dict["dotnet.runtime.js"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.runtime.js"), true);
dict["dotnet.runtime.js.map"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.runtime.js.map"), true);

if (IsFingerprintingEnabled)
{
string bootJsonPath = Path.Combine(paths.BinFrameworkDir, "dotnet.boot.js");
if (!File.Exists(bootJsonPath))
bootJsonPath = Path.Combine(paths.BinFrameworkDir, "dotnet.js"); // inline boot config

string bootJsonPath = GetBootConfigPath(paths.BinFrameworkDir, "dotnet.js");
BootJsonData bootJson = GetBootJson(bootJsonPath);
var keysToUpdate = new List<string>();
var updates = new List<(string oldKey, string newKey, (string fullPath, bool unchanged) value)>();
Expand Down Expand Up @@ -487,16 +483,59 @@ public void AssertIcuAssets(AssertBundleOptions assertOptions, BootJsonData boot
}
}

private BootJsonData GetBootJson(string bootJsonPath)
public BootJsonData GetBootJson(string bootJsonPath)
{
Assert.True(File.Exists(bootJsonPath), $"Expected to find {bootJsonPath}");
return ParseBootData(bootJsonPath);
}

public string GetBootConfigPath(string binFrameworkDir, string? bootConfigFileName = null)
{
string[] allDotnetFiles = [
"dotnet.runtime",
"dotnet.native",
"dotnet.native.worker",
"dotnet.diagnostics"
];

bootConfigFileName ??= "dotnet.js";

if (bootConfigFileName.EndsWith(".js"))
{
string bootFileNameWithoutExtension = Path.GetFileNameWithoutExtension(bootConfigFileName);
string bootFileExtension = Path.GetExtension(bootConfigFileName);
string? fingerprintedBootJsonPath = Directory
.EnumerateFiles(binFrameworkDir)
.FirstOrDefault(f =>
{
if (Path.GetExtension(f) != bootFileExtension)
return false;

string fileName = Path.GetFileName(f);
if (!fileName.StartsWith(bootFileNameWithoutExtension))
return false;

if (allDotnetFiles.Except([bootFileNameWithoutExtension]).Any(a => fileName.StartsWith(a)))
return false;

return true;
});

if (fingerprintedBootJsonPath == null)
throw new XunitException($"Could not find boot config '{bootConfigFileName}' with fingerprint in '{binFrameworkDir}'");

return fingerprintedBootJsonPath;
}
else
{
return Path.Combine(binFrameworkDir, bootConfigFileName);
}
}

public BootJsonData AssertBootJson(AssertBundleOptions options)
{
EnsureProjectDirIsSet();
string bootJsonPath = Path.Combine(options.BinFrameworkDir, options.BuildOptions.BootConfigFileName ?? "dotnet.js");
string bootJsonPath = GetBootConfigPath(options.BinFrameworkDir, options.BuildOptions.BootConfigFileName);
BootJsonData bootJson = GetBootJson(bootJsonPath);
string spcExpectedFilename = $"System.Private.CoreLib{WasmAssemblyExtension}";

Expand Down Expand Up @@ -527,8 +566,8 @@ public BootJsonData AssertBootJson(AssertBundleOptions options)
var knownSet = GetAllKnownDotnetFilesToFingerprintMap(options);
foreach (string expectedFilename in expected)
{
// FIXME: Find a systematic solution for skipping dotnet.js & dotnet.boot.js from boot json check
if (expectedFilename == "dotnet.js" || expectedFilename == "dotnet.boot.js" || Path.GetExtension(expectedFilename) == ".map")
// FIXME: Find a systematic solution for skipping dotnet.js from boot json check
if (expectedFilename == "dotnet.js" || Path.GetExtension(expectedFilename) == ".map")
continue;

bool expectFingerprint = knownSet[expectedFilename];
Expand Down Expand Up @@ -572,6 +611,21 @@ public BootJsonData AssertBootJson(AssertBundleOptions options)
}

public static BootJsonData ParseBootData(string bootConfigPath)
{
string jsonContent = GetBootJsonContent(bootConfigPath);
try
{
BootJsonData? config = JsonSerializer.Deserialize<BootJsonData>(jsonContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
Assert.NotNull(config);
return config!;
}
catch (JsonException e)
{
throw new XunitException($"Parsing config failed{Environment.NewLine}{Environment.NewLine}{jsonContent}", e);
}
}

public static string GetBootJsonContent(string bootConfigPath)
{
string startComment = "/*json-start*/";
string endComment = "/*json-end*/";
Expand All @@ -584,27 +638,10 @@ public static BootJsonData ParseBootData(string bootConfigPath)
// boot.js
int startJsonIndex = startCommentIndex + startComment.Length;
string jsonContent = moduleContent.Substring(startJsonIndex, endCommentIndex - startJsonIndex);
using var ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent));
ms.Position = 0;
return LoadConfig(ms);
}
else
{
using FileStream stream = File.OpenRead(bootConfigPath);
stream.Position = 0;
return LoadConfig(stream);
return jsonContent;
}

static BootJsonData LoadConfig(Stream stream)
{
var serializer = new DataContractJsonSerializer(
typeof(BootJsonData),
new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true });

var config = (BootJsonData?)serializer.ReadObject(stream);
Assert.NotNull(config);
return config;
}
return moduleContent;
}

private void AssertFileNames(IEnumerable<string> expected, IEnumerable<string> actual)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public async void RunOutOfAppBundle(Configuration config, bool aot)
{
ProjectInfo info = CopyTestAsset(config, aot, TestAsset.WasmBasicTestApp, "outofappbundle");
UpdateFile(Path.Combine("Common", "Program.cs"), s_mainReturns42);
(string _, string output) = PublishProject(info, config, new PublishOptions(AOT: aot));
(string _, string output) = PublishProject(info, config, new PublishOptions(AOT: aot, ExtraMSBuildArgs: "/p:OverrideHtmlAssetPlaceholders=false", AssertAppBundle: false));

string binFrameworkDir = GetBinFrameworkDir(config, forPublish: true);
string appBundleDir = Path.Combine(binFrameworkDir, "..");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected override IReadOnlyDictionary<string, bool> GetAllKnownDotnetFilesToFin
{
var result = new SortedDictionary<string, bool>()
{
{ "dotnet.js", false },
{ "dotnet.js", true },
{ "dotnet.js.map", false },
{ "dotnet.native.js", true },
{ "dotnet.native.js.symbols", false },
Expand All @@ -41,7 +41,7 @@ protected override IReadOnlyDictionary<string, bool> GetAllKnownDotnetFilesToFin
};

if (assertOptions.BuildOptions.BootConfigFileName?.EndsWith(".js") ?? false)
result[assertOptions.BuildOptions.BootConfigFileName] = false;
result[assertOptions.BuildOptions.BootConfigFileName] = true;

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
<PropertyGroup>
<TargetFramework>netX.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
<title>browser.0</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type='module' src="./main.js"></script>
<link rel="preload" id="webassembly" />
<script type="importmap"></script>
<script type='module' src="main#[.{fingerprint}].js"></script>
</head>

<body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<link rel="stylesheet" href="css/app.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<link href="BlazorBasicTestApp.styles.css" rel="stylesheet" />
<script type="importmap"></script>
</head>

<body>
Expand All @@ -26,7 +27,7 @@
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>

</html>
1 change: 1 addition & 0 deletions src/mono/wasm/testassets/LibraryMode/LibraryMode.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net10.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Library</OutputType>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>
</Project>

3 changes: 2 additions & 1 deletion src/mono/wasm/testassets/LibraryMode/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
<title>tmp</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type='module' src="./main.js"></script>
<script type="importmap"></script>
<script type="module" src="./main.js"></script>
</head>

<body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
<!-- The private field 'class member' is never used -->
<NoWarn>$(NoWarn);CS0169</NoWarn>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
<html>

<head>
<title>WasmLazyLoading</title>
<title>WasmBasicTestApp</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preload" id="webassembly" />
<script type="importmap"></script>
<script type='module' src="./main.js"></script>
<script type='module' src="./profiler.js"></script>
</head>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<WasmEnableThreads>true</WasmEnableThreads>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
<!-- nullablility warning, async warning -->
<NoWarn>$(NoWarn);CS8604;CS4014</NoWarn>
<StaticWebAssetBasePath>blazorclient</StaticWebAssetBasePath>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>BlazorHosted</title>
<base href="/blazorclient/" />
<script type="importmap"></script>
</head>

<body>
Expand All @@ -16,7 +17,7 @@
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>

</html>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
<!-- declare types in namespaces; ConfigureAwait; Prefer 'static readonly' fields; trim warnings -->
<NoWarn>$(NoWarn);CA1050;CA2007;CA1861;IL2104</NoWarn>
<WasmEnableThreads>true</WasmEnableThreads>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<title>WasmBrowser</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="importmap"></script>
<script type='module' src="./main.js"></script>
</head>

Expand Down