diff --git a/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs b/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs index 047f08d5dd..d3f1de66a9 100644 --- a/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs +++ b/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs @@ -88,6 +88,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build case "assembly": resourceList = resourceData.assembly; break; + case "dynamicAssembly": + resourceList = resourceData.dynamicAssembly; + break; case "pdb": resourceData.pdb ??= new ResourceHashesByNameDictionary(); resourceList = resourceData.pdb; @@ -207,6 +210,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build /// public ResourceHashesByNameDictionary assembly { get; set; } = new ResourceHashesByNameDictionary(); + /// + /// Assembly (.dll) resources that are loaded dynamically during runtime + /// + public ResourceHashesByNameDictionary dynamicAssembly { get; set; } = new ResourceHashesByNameDictionary(); + /// /// "debug" (.pdb) resources /// @@ -221,4 +229,4 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build } #pragma warning restore IDE1006 // Naming Styles } -} +} \ No newline at end of file diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets index 7d6eb810b4..c143445685 100644 --- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets @@ -129,6 +129,7 @@ <_BlazorOutputWithTargetPath Include="@(_BlazorResolvedAssembly)"> assembly pdb + dynamicAssembly %(FileName)%(Extension) $(_BlazorRuntimeBinOutputPath)%(FileName)%(Extension) diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildLazyLoadTest.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildLazyLoadTest.cs new file mode 100644 index 0000000000..dc6be8d80e --- /dev/null +++ b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildLazyLoadTest.cs @@ -0,0 +1,176 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; +using Xunit; +using static Microsoft.AspNetCore.Components.WebAssembly.Build.WebAssemblyRuntimePackage; + +namespace Microsoft.AspNetCore.Components.WebAssembly.Build +{ + public class BuildLazyLoadTest + { + [Fact] + public async Task Build_LazyLoadExplicitAssembly_Debug_Works() + { + // Arrange + using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Debug"; + + project.AddProjectFileContent( +@" + + + +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + var buildOutputDirectory = project.BuildOutputDirectory; + + // Verify that a blazor.boot.json file has been created + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + // And that the assembly is in the output + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.dll"); + + var bootJson = ReadBootJsonData(result, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json")); + + // And that it has been labelled as a dynamic assembly in the boot.json + var dynamicAssemblies = bootJson.resources.dynamicAssembly; + var assemblies = bootJson.resources.assembly; + + Assert.NotNull(dynamicAssemblies); + Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys); + Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); + + // App assembly should not be lazy loaded + Assert.DoesNotContain("standalone.dll", dynamicAssemblies.Keys); + Assert.Contains("standalone.dll", assemblies.Keys); + } + + [Fact] + public async Task Build_LazyLoadExplicitAssembly_Release_Works() + { + // Arrange + using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Release"; + + project.AddProjectFileContent( +@" + + + +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + var buildOutputDirectory = project.BuildOutputDirectory; + + // Verify that a blazor.boot.json file has been created + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + // And that the assembly is in the output + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.dll"); + + var bootJson = ReadBootJsonData(result, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json")); + + // And that it has been labelled as a dynamic assembly in the boot.json + var dynamicAssemblies = bootJson.resources.dynamicAssembly; + var assemblies = bootJson.resources.assembly; + + Assert.NotNull(dynamicAssemblies); + Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys); + Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); + + // App assembly should not be lazy loaded + Assert.DoesNotContain("standalone.dll", dynamicAssemblies.Keys); + Assert.Contains("standalone.dll", assemblies.Keys); + } + + [Fact] + public async Task Publish_LazyLoadExplicitAssembly_Debug_Works() + { + // Arrange + using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Debug"; + + project.AddProjectFileContent( +@" + + + +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + + var publishDirectory = project.PublishOutputDirectory; + + // Verify that a blazor.boot.json file has been created + Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "blazor.boot.json"); + // And that the assembly is in the output + Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.dll"); + + var bootJson = ReadBootJsonData(result, Path.Combine(publishDirectory, "wwwroot", "_framework", "blazor.boot.json")); + + // And that it has been labelled as a dynamic assembly in the boot.json + var dynamicAssemblies = bootJson.resources.dynamicAssembly; + var assemblies = bootJson.resources.assembly; + + Assert.NotNull(dynamicAssemblies); + Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys); + Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); + + // App assembly should not be lazy loaded + Assert.DoesNotContain("standalone.dll", dynamicAssemblies.Keys); + Assert.Contains("standalone.dll", assemblies.Keys); + } + + [Fact] + public async Task Publish_LazyLoadExplicitAssembly_Release_Works() + { + // Arrange + using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Release"; + + project.AddProjectFileContent( +@" + + + +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + + var publishDirectory = project.PublishOutputDirectory; + + // Verify that a blazor.boot.json file has been created + Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "blazor.boot.json"); + // And that the assembly is in the output + Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.dll"); + + var bootJson = ReadBootJsonData(result, Path.Combine(publishDirectory, "wwwroot", "_framework", "blazor.boot.json")); + + // And that it has been labelled as a dynamic assembly in the boot.json + var dynamicAssemblies = bootJson.resources.dynamicAssembly; + var assemblies = bootJson.resources.assembly; + + Assert.NotNull(dynamicAssemblies); + Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys); + Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); + + // App assembly should not be lazy loaded + Assert.DoesNotContain("standalone.dll", dynamicAssemblies.Keys); + Assert.Contains("standalone.dll", assemblies.Keys); + } + + private static GenerateBlazorBootJson.BootJsonData ReadBootJsonData(MSBuildResult result, string path) + { + return JsonSerializer.Deserialize( + File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)), + new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + } +}