diff --git a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/BootJsonData.cs b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/BootJsonData.cs index 87309e9b53a8..98ae53c693a9 100644 --- a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/BootJsonData.cs +++ b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/BootJsonData.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Runtime.Serialization; using System.Text.Json; +using System.Text.Json.Serialization; using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary; // For test purposes only. Actual build time implementation lives in runtime repository with WasmSDK @@ -41,7 +42,11 @@ public string entryAssembly /// and values are SHA-256 hashes formatted in prefixed base-64 style (e.g., 'sha256-abcdefg...') /// as used for subresource integrity checking. /// - public ResourcesData resources { get; set; } = new ResourcesData(); + [JsonIgnore] + public ResourcesData resources => (ResourcesData)resourcesRaw; + + [JsonPropertyName("resources")] + public object resourcesRaw { get; set; } /// /// Gets a value that determines whether to enable caching of the @@ -148,6 +153,9 @@ public class ResourcesData [DataMember(EmitDefaultValue = false)] public ResourceHashesByNameDictionary jsModuleWorker { get; set; } + [DataMember(EmitDefaultValue = false)] + public ResourceHashesByNameDictionary jsModuleDiagnostics { get; set; } + [DataMember(EmitDefaultValue = false)] public ResourceHashesByNameDictionary jsModuleNative { get; set; } @@ -158,16 +166,21 @@ public class ResourcesData public ResourceHashesByNameDictionary wasmNative { get; set; } [DataMember(EmitDefaultValue = false)] - public ResourceHashesByNameDictionary jsSymbols { get; set; } + public ResourceHashesByNameDictionary wasmSymbols { get; set; } [DataMember(EmitDefaultValue = false)] public ResourceHashesByNameDictionary icu { get; set; } + public ResourceHashesByNameDictionary coreAssembly { get; set; } = new ResourceHashesByNameDictionary(); + /// /// "assembly" (.dll) resources /// public ResourceHashesByNameDictionary assembly { get; set; } = new ResourceHashesByNameDictionary(); + [DataMember(EmitDefaultValue = false)] + public ResourceHashesByNameDictionary corePdb { get; set; } + /// /// "debug" (.pdb) resources /// @@ -212,6 +225,9 @@ public class ResourcesData [DataMember(EmitDefaultValue = false)] public Dictionary runtimeAssets { get; set; } + [DataMember(EmitDefaultValue = false)] + public Dictionary coreVfs { get; set; } + [DataMember(EmitDefaultValue = false)] public Dictionary vfs { get; set; } @@ -219,6 +235,134 @@ public class ResourcesData public List remoteSources { get; set; } } +public class AssetsData +{ + /// + /// Gets a hash of all resources + /// + public string hash { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List jsModuleWorker { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List jsModuleDiagnostics { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List jsModuleNative { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List jsModuleRuntime { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List wasmNative { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List wasmSymbols { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List icu { get; set; } + + /// + /// "assembly" (.dll) resources needed to start MonoVM + /// + public List coreAssembly { get; set; } = new(); + + /// + /// "assembly" (.dll) resources + /// + public List assembly { get; set; } = new(); + + /// + /// "debug" (.pdb) resources needed to start MonoVM + /// + [DataMember(EmitDefaultValue = false)] + public List corePdb { get; set; } + + /// + /// "debug" (.pdb) resources + /// + [DataMember(EmitDefaultValue = false)] + public List pdb { get; set; } + + /// + /// localization (.satellite resx) resources + /// + [DataMember(EmitDefaultValue = false)] + public Dictionary> satelliteResources { get; set; } + + /// + /// Assembly (.dll) resources that are loaded lazily during runtime + /// + [DataMember(EmitDefaultValue = false)] + public List lazyAssembly { get; set; } + + /// + /// JavaScript module initializers that Blazor will be in charge of loading. + /// Used in .NET < 8 + /// + [DataMember(EmitDefaultValue = false)] + public List libraryInitializers { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List modulesAfterConfigLoaded { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List modulesAfterRuntimeReady { get; set; } + + /// + /// Extensions created by users customizing the initialization process. The format of the file(s) + /// is up to the user. + /// + [DataMember(EmitDefaultValue = false)] + public Dictionary extensions { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List coreVfs { get; set; } + + [DataMember(EmitDefaultValue = false)] + public List vfs { get; set; } +} + +[DataContract] +public class JsAsset +{ + public string name { get; set; } + public string moduleExports { get; set; } +} + +[DataContract] +public class SymbolsAsset +{ + public string name { get; set; } +} + +[DataContract] +public class WasmAsset +{ + public string name { get; set; } + public string integrity { get; set; } + public string resolvedUrl { get; set; } +} + +[DataContract] +public class GeneralAsset +{ + public string virtualPath { get; set; } + public string name { get; set; } + public string integrity { get; set; } + public string resolvedUrl { get; set; } +} + +[DataContract] +public class VfsAsset +{ + public string virtualPath { get; set; } + public string name { get; set; } + public string integrity { get; set; } + public string resolvedUrl { get; set; } +} + public enum GlobalizationMode : int { // Note that the numeric values are serialized and used in JS code, so don't change them without also updating the JS code @@ -261,10 +405,65 @@ public class BootJsonDataLoader public static BootJsonData ParseBootData(string bootConfigPath) { string jsonContent = GetJsonContent(bootConfigPath); - BootJsonData config = JsonSerializer.Deserialize(jsonContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + options.Converters.Add(new ResourcesConverter()); + BootJsonData config = JsonSerializer.Deserialize(jsonContent, options); + if (config.resourcesRaw is AssetsData assets) + { + config.resourcesRaw = ConvertAssetsToResources(assets); + } + return config; } + private static ResourcesData ConvertAssetsToResources(AssetsData assets) + { + static Dictionary ConvertSatelliteResources(Dictionary> satelliteResources) + { + if (satelliteResources == null) + return null; + + var result = new Dictionary(); + foreach (var kvp in satelliteResources) + result[kvp.Key] = kvp.Value.ToDictionary(a => a.name, a => a.integrity); + + return result; + } + + static Dictionary ConvertVfsAssets(List vfsAssets) + { + return vfsAssets?.ToDictionary(a => a.virtualPath, a => new ResourceHashesByNameDictionary + { + { a.name, a.integrity } + }); + } + + var resources = new ResourcesData + { + hash = assets.hash, + jsModuleWorker = assets.jsModuleWorker?.ToDictionary(a => a.name, a => (string)null), + jsModuleDiagnostics = assets.jsModuleDiagnostics?.ToDictionary(a => a.name, a => (string)null), + jsModuleNative = assets.jsModuleNative?.ToDictionary(a => a.name, a => (string)null), + jsModuleRuntime = assets.jsModuleRuntime?.ToDictionary(a => a.name, a => (string)null), + wasmNative = assets.wasmNative?.ToDictionary(a => a.name, a => a.integrity), + wasmSymbols = assets.wasmSymbols?.ToDictionary(a => a.name, a => (string)null), + icu = assets.icu?.ToDictionary(a => a.name, a => a.integrity), + coreAssembly = assets.coreAssembly?.ToDictionary(a => a.name, a => a.integrity), + assembly = assets.assembly?.ToDictionary(a => a.name, a => a.integrity), + corePdb = assets.corePdb?.ToDictionary(a => a.name, a => a.integrity), + pdb = assets.pdb?.ToDictionary(a => a.name, a => a.integrity), + satelliteResources = ConvertSatelliteResources(assets.satelliteResources), + lazyAssembly = assets.lazyAssembly?.ToDictionary(a => a.name, a => a.integrity), + libraryInitializers = assets.libraryInitializers?.ToDictionary(a => a.name, a => (string)null), + modulesAfterConfigLoaded = assets.modulesAfterConfigLoaded?.ToDictionary(a => a.name, a => (string)null), + modulesAfterRuntimeReady = assets.modulesAfterRuntimeReady?.ToDictionary(a => a.name, a => (string)null), + extensions = assets.extensions, + coreVfs = ConvertVfsAssets(assets.coreVfs), + vfs = ConvertVfsAssets(assets.vfs) + }; + return resources; + } + public static string GetJsonContent(string bootConfigPath) { string startComment = "/*json-start*/"; @@ -284,3 +483,32 @@ public static string GetJsonContent(string bootConfigPath) return moduleContent; } } + +internal class ResourcesConverter : JsonConverter +{ + public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var nestedOptions = new JsonSerializerOptions(options); + nestedOptions.Converters.Remove(this); + + if (reader.TokenType == JsonTokenType.StartObject) + { + try + { + + return JsonSerializer.Deserialize(ref reader, nestedOptions)!; + } + catch + { + return JsonSerializer.Deserialize(ref reader, nestedOptions)!; + } + } + + return JsonSerializer.Deserialize(ref reader, nestedOptions)!; + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } +} diff --git a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPublishIntegrationTestBase.cs b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPublishIntegrationTestBase.cs index fad55742f540..60b933cd6629 100644 --- a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPublishIntegrationTestBase.cs +++ b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPublishIntegrationTestBase.cs @@ -16,7 +16,12 @@ protected static void VerifyBootManifestHashes(TestAsset testAsset, string blazo var bootManifestResolvedPath = Path.Combine(blazorPublishDirectory, "_framework", WasmBootConfigFileName); var bootManifest = BootJsonDataLoader.ParseBootData(bootManifestResolvedPath); + VerifyBootManifestHashes(testAsset, blazorPublishDirectory, bootManifest.resources.coreAssembly); VerifyBootManifestHashes(testAsset, blazorPublishDirectory, bootManifest.resources.assembly); + if (bootManifest.resources.corePdb != null) + { + VerifyBootManifestHashes(testAsset, blazorPublishDirectory, bootManifest.resources.corePdb); + } if (bootManifest.resources.pdb != null) { VerifyBootManifestHashes(testAsset, blazorPublishDirectory, bootManifest.resources.pdb); @@ -33,14 +38,6 @@ protected static void VerifyBootManifestHashes(TestAsset testAsset, string blazo { VerifyBootManifestHashes(testAsset, blazorPublishDirectory, bootManifest.resources.wasmNative); } - if (bootManifest.resources.jsModuleNative != null) - { - VerifyBootManifestHashes(testAsset, blazorPublishDirectory, bootManifest.resources.jsModuleNative); - } - if (bootManifest.resources.jsModuleRuntime != null) - { - VerifyBootManifestHashes(testAsset, blazorPublishDirectory, bootManifest.resources.jsModuleRuntime); - } if (bootManifest.resources.satelliteResources != null) {