diff --git a/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHost.cs b/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHost.cs index 3a2ccfbaaee2..b90878fdde36 100644 --- a/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHost.cs +++ b/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHost.cs @@ -19,6 +19,11 @@ internal class WebAssemblyHost : IWebAssemblyHost public WebAssemblyHost(IServiceProvider services, IJSRuntime runtime) { + // To ensure JS-invoked methods don't get linked out, have a reference to their enclosing types + GC.KeepAlive(typeof(EntrypointInvoker)); + GC.KeepAlive(typeof(JSInteropMethods)); + GC.KeepAlive(typeof(WebAssemblyEventDispatcher)); + Services = services ?? throw new ArgumentNullException(nameof(services)); _runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); } diff --git a/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs new file mode 100644 index 000000000000..8a56b7fc3deb --- /dev/null +++ b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs @@ -0,0 +1,48 @@ +// 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.IO; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.AspNetCore.Blazor.Build.Tasks +{ + public class GenerateTypeGranularityLinkingConfig : Task + { + [Required] + public ITaskItem[] Assemblies { get; set; } + + [Required] + public string OutputPath { get; set; } + + public override bool Execute() + { + var linkerElement = new XElement("linker", + new XComment(" THIS IS A GENERATED FILE - DO NOT EDIT MANUALLY ")); + + foreach (var assembly in Assemblies) + { + var assemblyElement = CreateTypeGranularityConfig(assembly); + linkerElement.Add(assemblyElement); + } + + using var fileStream = File.Open(OutputPath, FileMode.Create); + new XDocument(linkerElement).Save(fileStream); + + return true; + } + + private XElement CreateTypeGranularityConfig(ITaskItem assembly) + { + // We match all types in the assembly, and for each one, tell the linker to preserve all + // its members (preserve=all) but only if there's some reference to the type (required=false) + return new XElement("assembly", + new XAttribute("fullname", Path.GetFileNameWithoutExtension(assembly.ItemSpec)), + new XElement("type", + new XAttribute("fullname", "*"), + new XAttribute("preserve", "all"), + new XAttribute("required", "false"))); + } + } +} diff --git a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets index 0a3ba70a2374..4bdb2f431156 100644 --- a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets @@ -160,6 +160,7 @@ + - <_BlazorDependencyAssembly Include="@(_BlazorDependencyInput)" IsLinkable="$([System.String]::Copy('%(FileName)').StartsWith('System.'))" /> + <_BlazorDependencyAssembly Include="@(_BlazorDependencyInput)" /> + <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('System.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.'))" /> <_BlazorAssemblyToLink Include="@(_WebAssemblyBCLAssembly)" /> <_BlazorAssemblyToLink Include="@(_BlazorDependencyAssembly)" Condition="'%(_BlazorDependencyAssembly.IsLinkable)' == 'true'" /> @@ -201,6 +205,15 @@ <_DotNetHostFileName Condition=" '$(OS)' == 'Windows_NT' ">dotnet.exe + + <_TypeGranularityLinkingConfig>$(BlazorIntermediateOutputPath)linker.typegranularityconfig.xml + + + + + + +