diff --git a/src/mono/browser/runtime/lazyLoading.ts b/src/mono/browser/runtime/lazyLoading.ts index 3d075b3005a8e..9787c18ac33ec 100644 --- a/src/mono/browser/runtime/lazyLoading.ts +++ b/src/mono/browser/runtime/lazyLoading.ts @@ -7,17 +7,24 @@ import { AssetEntry } from "./types"; export async function loadLazyAssembly (assemblyNameToLoad: string): Promise { const resources = loaderHelpers.config.resources!; - const originalAssemblyName = assemblyNameToLoad; const lazyAssemblies = resources.lazyAssembly; if (!lazyAssemblies) { throw new Error("No assemblies have been marked as lazy-loadable. Use the 'BlazorWebAssemblyLazyLoad' item group in your project file to enable lazy loading an assembly."); } + let assemblyNameWithoutExtension = assemblyNameToLoad; + if (assemblyNameToLoad.endsWith(".dll")) + assemblyNameWithoutExtension = assemblyNameToLoad.substring(0, assemblyNameToLoad.length - 4); + else if (assemblyNameToLoad.endsWith(".wasm")) + assemblyNameWithoutExtension = assemblyNameToLoad.substring(0, assemblyNameToLoad.length - 5); + + const assemblyNameToLoadDll = assemblyNameWithoutExtension + ".dll"; + const assemblyNameToLoadWasm = assemblyNameWithoutExtension + ".wasm"; if (loaderHelpers.config.resources!.fingerprinting) { const map = loaderHelpers.config.resources!.fingerprinting; for (const fingerprintedName in map) { const nonFingerprintedName = map[fingerprintedName]; - if (nonFingerprintedName == assemblyNameToLoad) { + if (nonFingerprintedName == assemblyNameToLoadDll || nonFingerprintedName == assemblyNameToLoadWasm) { assemblyNameToLoad = fingerprintedName; break; } @@ -25,7 +32,13 @@ export async function loadLazyAssembly (assemblyNameToLoad: string): Promise LoadLazyAssemblyBeforeItIsNeededData() + { + string[] data = ["wasm", "dll", "NoExtension"]; + return data.Select(d => new object[] { d, data }); + } + + [Theory, TestCategory("no-fingerprinting")] + [MemberData(nameof(LoadLazyAssemblyBeforeItIsNeededData))] + public async Task LoadLazyAssemblyBeforeItIsNeeded(string lazyLoadingTestExtension, string[] allLazyLoadingTestExtensions) { CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests", "App"); - BuildProject("Debug"); + BuildProject("Debug", extraArgs: $"-p:LazyLoadingTestExtension={lazyLoadingTestExtension}"); - var result = await RunSdkStyleAppForBuild(new(Configuration: "Debug", TestScenario: "LazyLoadingTest")); + // We are running the app and passing all possible lazy extensions to test matrix of all possibilities. + // We don't need to rebuild the application to test how client is trying to load the assembly. + foreach (var clientLazyLoadingTestExtension in allLazyLoadingTestExtensions) + { + var result = await RunSdkStyleAppForBuild(new( + Configuration: "Debug", + TestScenario: "LazyLoadingTest", + BrowserQueryString: new Dictionary { ["lazyLoadingTestExtension"] = clientLazyLoadingTestExtension } + )); - Assert.True(result.TestOutput.Any(m => m.Contains("FirstName")), "The lazy loading test didn't emit expected message with JSON"); - Assert.True(result.ConsoleOutput.Any(m => m.Contains("Attempting to download") && m.Contains("_framework/Json.") && m.Contains(".pdb")), "The lazy loading test didn't load PDB"); + Assert.True(result.TestOutput.Any(m => m.Contains("FirstName")), "The lazy loading test didn't emit expected message with JSON"); + Assert.True(result.ConsoleOutput.Any(m => m.Contains("Attempting to download") && m.Contains("_framework/Json.") && m.Contains(".pdb")), "The lazy loading test didn't load PDB"); + } } [Fact] diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj b/src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj index 04469138d9803..a62c057733b4f 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj @@ -6,11 +6,19 @@ true + + + .dll + .wasm + + $(WasmAssemblyExtension) + + - + diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index 85fd83270b273..7fccb3c8e473d 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -138,7 +138,23 @@ try { break; case "LazyLoadingTest": if (params.get("loadRequiredAssembly") !== "false") { - await INTERNAL.loadLazyAssembly(`Json${assemblyExtension}`); + let lazyAssemblyExtension = assemblyExtension; + switch (params.get("lazyLoadingTestExtension")) { + case "wasm": + lazyAssemblyExtension = ".wasm"; + break; + case "dll": + lazyAssemblyExtension = ".dll"; + break; + case "NoExtension": + lazyAssemblyExtension = ""; + break; + default: + lazyAssemblyExtension = assemblyExtension; + break; + } + + await INTERNAL.loadLazyAssembly(`Json${lazyAssemblyExtension}`); } exports.LazyLoadingTest.Run(); exit(0); diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs index 63cee84d412a8..748671aeec898 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs @@ -176,6 +176,15 @@ public void WriteBootJson(Stream output, string entryAssemblyName) { var endpointByAsset = Endpoints.ToDictionary(e => e.GetMetadata("AssetFile")); + var lazyLoadAssembliesWithoutExtension = (LazyLoadedAssemblies ?? Array.Empty()).ToDictionary(l => + { + var extension = Path.GetExtension(l.ItemSpec); + if (extension == ".dll" || extension == Utils.WebcilInWasmExtension) + return Path.GetFileNameWithoutExtension(l.ItemSpec); + + return l.ItemSpec; + }); + var remainingLazyLoadAssemblies = new List(LazyLoadedAssemblies ?? Array.Empty()); var resourceData = result.resources; @@ -194,7 +203,7 @@ public void WriteBootJson(Stream output, string entryAssemblyName) var resourceName = Path.GetFileName(resource.GetMetadata("OriginalItemSpec")); var resourceRoute = Path.GetFileName(endpointByAsset[resource.ItemSpec].ItemSpec); - if (TryGetLazyLoadedAssembly(resourceName, out var lazyLoad)) + if (TryGetLazyLoadedAssembly(lazyLoadAssembliesWithoutExtension, resourceName, out var lazyLoad)) { MapFingerprintedAsset(resourceData, resourceRoute, resourceName); Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as a lazy loaded assembly.", resource.ItemSpec); @@ -220,7 +229,7 @@ public void WriteBootJson(Stream output, string entryAssemblyName) else if (string.Equals("symbol", assetTraitValue, StringComparison.OrdinalIgnoreCase)) { MapFingerprintedAsset(resourceData, resourceRoute, resourceName); - if (TryGetLazyLoadedAssembly($"{fileName}.dll", out _) || TryGetLazyLoadedAssembly($"{fileName}{Utils.WebcilInWasmExtension}", out _)) + if (TryGetLazyLoadedAssembly(lazyLoadAssembliesWithoutExtension, fileName, out _)) { Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as a lazy loaded symbols file.", resource.ItemSpec); resourceData.lazyAssembly ??= new ResourceHashesByNameDictionary(); @@ -463,9 +472,13 @@ private void AddToAdditionalResources(ITaskItem resource, Dictionary lazyLoadAssembliesNoExtension, string fileName, out ITaskItem lazyLoadedAssembly) { - return (lazyLoadedAssembly = LazyLoadedAssemblies?.SingleOrDefault(a => a.ItemSpec == fileName)) != null; + var extension = Path.GetExtension(fileName); + if (extension == ".dll" || extension == Utils.WebcilInWasmExtension) + fileName = Path.GetFileNameWithoutExtension(fileName); + + return lazyLoadAssembliesNoExtension.TryGetValue(fileName, out lazyLoadedAssembly); } private Version? parsedTargetFrameworkVersion;