ソースを参照

Merge pull request #17562 from aspnet/prkrishn/merge-blazor-wasm

Merge blazor-wasm -> master
Pranav K 6 年 前
コミット
e72223eaf5
100 ファイル変更3785 行追加1771 行削除
  1. 1 0
      .azure/pipelines/ci.yml
  2. 288 288
      eng/Baseline.Designer.props
  3. 75 77
      eng/Baseline.xml
  4. 1 0
      eng/Build.props
  5. 3 2
      eng/ProjectReferences.props
  6. 2 2
      eng/Version.Details.xml
  7. 1 1
      eng/Versions.props
  8. 0 1
      eng/tools/BaselineGenerator/BaselineGenerator.csproj
  9. 3 3
      src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.csproj
  10. 0 0
      src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.netstandard2.1.cs
  11. 1 1
      src/Components/Blazor/Blazor/src/Microsoft.AspNetCore.Blazor.csproj
  12. 0 61
      src/Components/Blazor/Build/src/Cli/Commands/ResolveRuntimeDependenciesCommand.cs
  13. 0 68
      src/Components/Blazor/Build/src/Cli/Commands/WriteBootJsonCommand.cs
  14. 0 33
      src/Components/Blazor/Build/src/Cli/Program.cs
  15. 0 95
      src/Components/Blazor/Build/src/Core/BootJsonWriter.cs
  16. 0 17
      src/Components/Blazor/Build/src/Core/EmbeddedResources/EmbeddedResourceInfo.cs
  17. 0 12
      src/Components/Blazor/Build/src/Core/EmbeddedResources/EmbeddedResourceKind.cs
  18. 0 137
      src/Components/Blazor/Build/src/Core/EmbeddedResources/EmbeddedResourcesProcessor.cs
  19. 0 165
      src/Components/Blazor/Build/src/Core/RuntimeDependenciesResolver.cs
  20. 45 15
      src/Components/Blazor/Build/src/Microsoft.AspNetCore.Blazor.Build.csproj
  21. 2 2
      src/Components/Blazor/Build/src/Microsoft.AspNetCore.Blazor.Build.nuspec
  22. 25 0
      src/Components/Blazor/Build/src/ReferenceBlazorBuildFromSource.props
  23. 10 18
      src/Components/Blazor/Build/src/ReferenceFromSource.props
  24. 56 0
      src/Components/Blazor/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs
  25. 189 0
      src/Components/Blazor/Build/src/Tasks/BlazorILLink.cs
  26. 74 0
      src/Components/Blazor/Build/src/Tasks/GenerateBlazorBootJson.cs
  27. 203 0
      src/Components/Blazor/Build/src/Tasks/ResolveBlazorRuntimeDependencies.cs
  28. 17 17
      src/Components/Blazor/Build/src/targets/All.targets
  29. 9 11
      src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.props
  30. 159 530
      src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets
  31. 13 5
      src/Components/Blazor/Build/src/targets/Publish.targets
  32. 37 0
      src/Components/Blazor/Build/src/targets/StaticWebAssets.targets
  33. 38 0
      src/Components/Blazor/Build/test/BlazorCreateRootDescriptorFileTest.cs
  34. 21 42
      src/Components/Blazor/Build/test/BootJsonWriterTest.cs
  35. 950 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/Assert.cs
  36. 40 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/BuildIncrementalismTest.cs
  37. 74 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs
  38. 74 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/FileThumbPrint.cs
  39. 282 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/MSBuildProcessManager.cs
  40. 28 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/MSBuildResult.cs
  41. 211 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/ProjectDirectory.cs
  42. 21 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/ProjectDirectoryTest.cs
  43. 195 0
      src/Components/Blazor/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs
  44. 26 1
      src/Components/Blazor/Build/test/Microsoft.AspNetCore.Blazor.Build.Tests.csproj
  45. 2 2
      src/Components/Blazor/Build/test/RuntimeDependenciesResolverTest.cs
  46. 31 0
      src/Components/Blazor/Build/testassets/Directory.Build.props
  47. 2 0
      src/Components/Blazor/Build/testassets/Directory.Build.targets
  48. 15 0
      src/Components/Blazor/Build/testassets/blazorhosted/Program.cs
  49. 12 0
      src/Components/Blazor/Build/testassets/blazorhosted/blazorhosted.csproj
  50. 8 0
      src/Components/Blazor/Build/testassets/razorclasslibrary/RazorClassLibrary.csproj
  51. 1 0
      src/Components/Blazor/Build/testassets/razorclasslibrary/wwwroot/styles.css
  52. 0 0
      src/Components/Blazor/Build/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js
  53. 8 0
      src/Components/Blazor/Build/testassets/standalone/App.razor
  54. 5 0
      src/Components/Blazor/Build/testassets/standalone/Pages/Index.razor
  55. 10 0
      src/Components/Blazor/Build/testassets/standalone/Program.cs
  56. 2 0
      src/Components/Blazor/Build/testassets/standalone/_Imports.razor
  57. 20 0
      src/Components/Blazor/Build/testassets/standalone/standalone.csproj
  58. 24 0
      src/Components/Blazor/Build/testassets/standalone/wwwroot/index.html
  59. 1 0
      src/Components/Blazor/Directory.Build.targets
  60. 0 10
      src/Components/Blazor/Http/ref/Microsoft.AspNetCore.Blazor.HttpClient.csproj
  61. 0 18
      src/Components/Blazor/Http/ref/Microsoft.AspNetCore.Blazor.HttpClient.netstandard2.0.cs
  62. 1 0
      src/Components/Blazor/Http/src/Microsoft.AspNetCore.Blazor.HttpClient.csproj
  63. 25 0
      src/Components/Blazor/Mono.WebAssembly.Interop/src/InternalCalls.cs
  64. 17 0
      src/Components/Blazor/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj
  65. 157 0
      src/Components/Blazor/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs
  66. 0 16
      src/Components/Blazor/Server/ref/Microsoft.AspNetCore.Blazor.Server.csproj
  67. 0 22
      src/Components/Blazor/Server/ref/Microsoft.AspNetCore.Blazor.Server.netcoreapp.cs
  68. 2 1
      src/Components/Blazor/Server/src/Microsoft.AspNetCore.Blazor.Server.csproj
  69. 117 17
      src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs
  70. 1 1
      src/Components/Blazor/Templates/src/content/BlazorWasm-CSharp/Client/BlazorWasm-CSharp.Client.csproj
  71. 1 1
      src/Components/Blazor/Templates/src/content/BlazorWasm-CSharp/Shared/BlazorWasm-CSharp.Shared.csproj
  72. 1 1
      src/Components/Blazor/Validation/src/Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj
  73. 1 14
      src/Components/Blazor/testassets/HostedInAspNet.Client/HostedInAspNet.Client.csproj
  74. 1 1
      src/Components/Blazor/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj
  75. 1 1
      src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Microsoft.AspNetCore.Blazor.E2EPerformance.csproj
  76. 1 0
      src/Components/Blazor/testassets/MonoSanity/MonoSanity.csproj
  77. 1 1
      src/Components/Blazor/testassets/MonoSanityClient/MonoSanityClient.csproj
  78. 1 1
      src/Components/Blazor/testassets/StandaloneApp/StandaloneApp.csproj
  79. 18 0
      src/Components/Components.sln
  80. 1 0
      src/Components/ComponentsNoDeps.slnf
  81. 4 0
      src/Components/Directory.Build.props
  82. 20 0
      src/Components/Directory.Build.targets
  83. 2 0
      src/Components/Web.JS/Microsoft.AspNetCore.Components.Web.JS.npmproj
  84. 0 0
      src/Components/Web.JS/dist/Release/blazor.server.js
  85. 0 0
      src/Components/Web.JS/dist/Release/blazor.webassembly.js
  86. 4 37
      src/Components/Web.JS/src/Boot.WebAssembly.ts
  87. 9 15
      src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts
  88. 2 0
      src/Components/Web.JS/src/Platform/Mono/MonoTypes.d.ts
  89. 1 1
      src/Components/Web.JS/src/Platform/Platform.ts
  90. 1 1
      src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj
  91. 1 1
      src/Components/test/testassets/TestServer/Components.TestServer.csproj
  92. 10 0
      src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.cpp
  93. 3 0
      src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.h
  94. 8 0
      src/Servers/IIS/AspNetCoreModuleV2/CommonLib/config_utility.h
  95. 14 1
      src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp
  96. 1 0
      src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp
  97. 28 4
      src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp
  98. 2 0
      src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.h
  99. 6 0
      src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp
  100. 7 0
      src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.h

+ 1 - 0
.azure/pipelines/ci.yml

@@ -7,6 +7,7 @@ trigger:
   batch: true
   branches:
     include:
+    - blazor-wasm
     - master
     - release/*
 

+ 288 - 288
eng/Baseline.Designer.props

@@ -2,7 +2,7 @@
 <Project>
   <PropertyGroup>
     <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
-    <AspNetCoreBaselineVersion>3.0.0</AspNetCoreBaselineVersion>
+    <AspNetCoreBaselineVersion>3.1.0</AspNetCoreBaselineVersion>
   </PropertyGroup>
   <!-- Package: AspNetCoreRuntime.3.0.x64-->
   <PropertyGroup Condition=" '$(PackageId)' == 'AspNetCoreRuntime.3.0.x64' ">
@@ -16,132 +16,132 @@
   <ItemGroup Condition=" '$(PackageId)' == 'AspNetCoreRuntime.3.0.x86' AND '$(TargetFramework)' == 'net461' " />
   <!-- Package: dotnet-sql-cache-->
   <PropertyGroup Condition=" '$(PackageId)' == 'dotnet-sql-cache' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <!-- Package: Microsoft.AspNetCore.ApiAuthorization.IdentityServer-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.ApiAuthorization.IdentityServer' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.ApiAuthorization.IdentityServer' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.ApiAuthorization.IdentityServer' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="[3.1.0, )" />
     <BaselinePackageReference Include="IdentityServer4" Version="[3.0.0, )" />
     <BaselinePackageReference Include="IdentityServer4.AspNetIdentity" Version="[3.0.0, )" />
     <BaselinePackageReference Include="IdentityServer4.EntityFramework" Version="[3.0.0, )" />
     <BaselinePackageReference Include="IdentityServer4.EntityFramework.Storage" Version="[3.0.0, )" />
     <BaselinePackageReference Include="IdentityServer4.Storage" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Http" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Http" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.App.Runtime.win-x64-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.App.Runtime.win-x64' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <!-- Package: Microsoft.AspNetCore.Authentication.AzureAD.UI-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.AzureAD.UI' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.AzureAD.UI' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.AzureAD.UI' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Authentication.AzureADB2C.UI-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.AzureADB2C.UI' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.AzureADB2C.UI' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.AzureADB2C.UI' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Authentication.Certificate-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Certificate' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Certificate' AND '$(TargetFramework)' == 'netcoreapp3.0' " />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Certificate' AND '$(TargetFramework)' == 'netcoreapp3.1' " />
   <!-- Package: Microsoft.AspNetCore.Authentication.Facebook-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Facebook' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Facebook' AND '$(TargetFramework)' == 'netcoreapp3.0' " />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Facebook' AND '$(TargetFramework)' == 'netcoreapp3.1' " />
   <!-- Package: Microsoft.AspNetCore.Authentication.Google-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Google' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Google' AND '$(TargetFramework)' == 'netcoreapp3.0' " />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Google' AND '$(TargetFramework)' == 'netcoreapp3.1' " />
   <!-- Package: Microsoft.AspNetCore.Authentication.JwtBearer-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.JwtBearer' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.JwtBearer' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.JwtBearer' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
     <BaselinePackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="[5.5.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Authentication.MicrosoftAccount-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.MicrosoftAccount' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.MicrosoftAccount' AND '$(TargetFramework)' == 'netcoreapp3.0' " />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.MicrosoftAccount' AND '$(TargetFramework)' == 'netcoreapp3.1' " />
   <!-- Package: Microsoft.AspNetCore.Authentication.Negotiate-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Negotiate' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Negotiate' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Negotiate' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Authentication.OpenIdConnect-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.OpenIdConnect' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.OpenIdConnect' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.OpenIdConnect' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
     <BaselinePackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="[5.5.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Authentication.Twitter-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Twitter' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Twitter' AND '$(TargetFramework)' == 'netcoreapp3.0' " />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.Twitter' AND '$(TargetFramework)' == 'netcoreapp3.1' " />
   <!-- Package: Microsoft.AspNetCore.Authentication.WsFederation-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.WsFederation' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.WsFederation' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authentication.WsFederation' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
     <BaselinePackageReference Include="Microsoft.IdentityModel.Protocols.WsFederation" Version="[5.5.0, )" />
     <BaselinePackageReference Include="System.IdentityModel.Tokens.Jwt" Version="[5.5.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Authorization-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authorization' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authorization' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Metadata" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authorization' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Metadata" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Authorization' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Metadata" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Metadata" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.AzureAppServices.HostingStartup-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.AzureAppServices.HostingStartup' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.AzureAppServices.HostingStartup' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.AzureAppServicesIntegration" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.AzureAppServices.HostingStartup' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.AzureAppServicesIntegration" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.AzureAppServices.SiteExtension-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.AzureAppServices.SiteExtension' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.AzureAppServices.SiteExtension' AND '$(TargetFramework)' == 'net461' ">
-    <BaselinePackageReference Include="Microsoft.Web.Xdt.Extensions" Version="[3.0.0-rc2.19465.2, )" />
+    <BaselinePackageReference Include="Microsoft.Web.Xdt.Extensions" Version="[3.1.0-rtm.19566.1, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.AzureAppServicesIntegration-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.AzureAppServicesIntegration' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.AzureAppServicesIntegration' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.AzureAppServicesIntegration' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Blazor-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Blazor' ">
@@ -186,510 +186,510 @@
   </PropertyGroup>
   <!-- Package: Microsoft.AspNetCore.Components-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Analyzers" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.JSInterop" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.ComponentModel.Annotations" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Analyzers" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.JSInterop" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.ComponentModel.Annotations" Version="[4.7.0, )" />
   </ItemGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Analyzers" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.JSInterop" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Analyzers" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.JSInterop" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Components.Analyzers-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Analyzers' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <!-- Package: Microsoft.AspNetCore.Components.Authorization-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Authorization' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Authorization' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Authorization' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Authorization' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Components.Forms-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Forms' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Forms' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Forms' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Forms' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.ComponentModel.Annotations" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.ComponentModel.Annotations" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Components.Web-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Web' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Web' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Forms" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.JSInterop" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Web' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Forms" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.JSInterop" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.Web' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Forms" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.JSInterop" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Forms" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.JSInterop" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.ConcurrencyLimiter-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.ConcurrencyLimiter' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.ConcurrencyLimiter' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.ConcurrencyLimiter' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Connections.Abstractions-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Connections.Abstractions' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Connections.Abstractions' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Features" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.6.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Connections.Abstractions' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Features" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.7.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Connections.Abstractions' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Features" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="[1.0.0, )" />
-    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Features" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="[1.1.0, )" />
+    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.7.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Connections.Abstractions' AND '$(TargetFramework)' == 'netstandard2.1' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Features" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Features" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Cryptography.Internal-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.Internal' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.Internal' AND '$(TargetFramework)' == 'netstandard2.0' " />
   <!-- Package: Microsoft.AspNetCore.Cryptography.KeyDerivation-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.KeyDerivation' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.KeyDerivation' AND '$(TargetFramework)' == 'netcoreapp2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.KeyDerivation' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.DataProtection-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
-  </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Win32.Registry" Version="[4.6.0, )" />
-    <BaselinePackageReference Include="System.Security.Cryptography.Xml" Version="[4.6.0, )" />
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Win32.Registry" Version="[4.7.0, )" />
+    <BaselinePackageReference Include="System.Security.Cryptography.Xml" Version="[4.7.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Win32.Registry" Version="[4.6.0, )" />
-    <BaselinePackageReference Include="System.Security.Cryptography.Xml" Version="[4.6.0, )" />
-    <BaselinePackageReference Include="System.Security.Principal.Windows" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Win32.Registry" Version="[4.7.0, )" />
+    <BaselinePackageReference Include="System.Security.Cryptography.Xml" Version="[4.7.0, )" />
+    <BaselinePackageReference Include="System.Security.Principal.Windows" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.DataProtection.Abstractions-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Abstractions' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Abstractions' AND '$(TargetFramework)' == 'netstandard2.0' " />
   <!-- Package: Microsoft.AspNetCore.DataProtection.AzureKeyVault-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.AzureKeyVault' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.AzureKeyVault' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.1.0, )" />
     <BaselinePackageReference Include="Microsoft.Azure.KeyVault" Version="[2.3.2, )" />
     <BaselinePackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="[3.19.8, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.DataProtection.AzureStorage-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.AzureStorage' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.AzureStorage' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.1.0, )" />
     <BaselinePackageReference Include="Microsoft.Azure.Storage.Blob" Version="[10.0.1, )" />
     <BaselinePackageReference Include="Microsoft.Data.OData" Version="[5.8.4, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.DataProtection.EntityFrameworkCore-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.EntityFrameworkCore' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.EntityFrameworkCore' AND '$(TargetFramework)' == 'netstandard2.1' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.DataProtection.Extensions-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Extensions' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Extensions' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Extensions' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Extensions' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.DataProtection.StackExchangeRedis-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.StackExchangeRedis' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.StackExchangeRedis' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[3.1.0, )" />
     <BaselinePackageReference Include="StackExchange.Redis" Version="[2.0.593, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.HeaderPropagation-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.HeaderPropagation' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.HeaderPropagation' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Http" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.HeaderPropagation' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Http" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Hosting.WindowsServices-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Hosting.WindowsServices' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Hosting.WindowsServices' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="System.ServiceProcess.ServiceController" Version="[4.6.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Hosting.WindowsServices' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="System.ServiceProcess.ServiceController" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Http.Connections.Client-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections.Client' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections.Client' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Common" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Common" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections.Client' AND '$(TargetFramework)' == 'netstandard2.1' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Common" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Common" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Http.Connections.Common-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections.Common' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections.Common' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections.Common' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections.Common' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.Text.Json" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.Text.Json" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Http.Features-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Features' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Features' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Primitives" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.6.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Features' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.Primitives" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.7.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Features' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Primitives" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Primitives" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Identity.EntityFrameworkCore-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.EntityFrameworkCore' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.EntityFrameworkCore' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Stores" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.EntityFrameworkCore' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Stores" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.EntityFrameworkCore' AND '$(TargetFramework)' == 'netstandard2.1' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Stores" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Stores" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Identity.Specification.Tests-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.Specification.Tests' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.Specification.Tests' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Testing" Version="[3.0.0-rc2.19463.5, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Configuration" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.Specification.Tests' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Testing" Version="[3.1.0-rtm.19565.4, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Configuration" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.1.0, )" />
     <BaselinePackageReference Include="xunit.assert" Version="[2.4.1, )" />
     <BaselinePackageReference Include="xunit.extensibility.core" Version="[2.4.1, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Identity.UI-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.UI' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.UI' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Stores" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Identity.UI' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Stores" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="[3.1.0, )" />
     <BaselinePackageReference Include="Newtonsoft.Json" Version="[12.0.2, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.JsonPatch-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.JsonPatch' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.JsonPatch' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.CSharp" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.CSharp" Version="[4.7.0, )" />
     <BaselinePackageReference Include="Newtonsoft.Json" Version="[12.0.2, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Metadata-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Metadata' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Metadata' AND '$(TargetFramework)' == 'netstandard2.0' " />
   <!-- Package: Microsoft.AspNetCore.MiddlewareAnalysis-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.MiddlewareAnalysis' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.MiddlewareAnalysis' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.MiddlewareAnalysis' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Mvc.NewtonsoftJson-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.NewtonsoftJson' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.NewtonsoftJson' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.NewtonsoftJson' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="[3.1.0, )" />
     <BaselinePackageReference Include="Newtonsoft.Json" Version="[12.0.2, )" />
     <BaselinePackageReference Include="Newtonsoft.Json.Bson" Version="[1.0.2, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.CodeAnalysis.Razor" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyModel" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.CodeAnalysis.Razor" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyModel" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Mvc.Testing-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.Testing' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.Testing' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.TestHost" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyModel" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Hosting" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Mvc.Testing' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.TestHost" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyModel" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Hosting" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.NodeServices-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.NodeServices' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.NodeServices' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Console" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.NodeServices' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Console" Version="[3.1.0, )" />
     <BaselinePackageReference Include="Newtonsoft.Json" Version="[12.0.2, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.Owin-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Owin' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Owin' AND '$(TargetFramework)' == 'netcoreapp3.0' " />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Owin' AND '$(TargetFramework)' == 'netcoreapp3.1' " />
   <!-- Package: Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.1.0, )" />
     <BaselinePackageReference Include="Libuv" Version="[1.10.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SignalR.Client-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Client' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Client' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Client.Core" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Client" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Client.Core" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Client" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SignalR.Client.Core-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Client.Core' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Client.Core' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="[1.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.Threading.Channels" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="[1.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.Threading.Channels" Version="[4.7.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Client.Core' AND '$(TargetFramework)' == 'netstandard2.1' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.Threading.Channels" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.Threading.Channels" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SignalR.Common-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Common' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Common' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Common' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Common' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="System.Text.Json" Version="[4.6.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="System.Text.Json" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SignalR.Protocols.Json-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Protocols.Json' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Protocols.Json' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Protocols.Json' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Protocols.Json' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SignalR.Protocols.MessagePack-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Protocols.MessagePack' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Protocols.MessagePack' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.1.0, )" />
     <BaselinePackageReference Include="MessagePack" Version="[1.7.3.7, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.1.0, )" />
     <BaselinePackageReference Include="Newtonsoft.Json" Version="[12.0.2, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SignalR.Specification.Tests-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Specification.Tests' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Specification.Tests' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Specification.Tests' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[3.1.0, )" />
     <BaselinePackageReference Include="xunit.assert" Version="[2.4.1, )" />
     <BaselinePackageReference Include="xunit.extensibility.core" Version="[2.4.1, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SignalR.StackExchangeRedis-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.StackExchangeRedis' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.StackExchangeRedis' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.StackExchangeRedis' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
     <BaselinePackageReference Include="MessagePack" Version="[1.7.3.7, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
     <BaselinePackageReference Include="StackExchange.Redis" Version="[2.0.593, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SpaServices-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SpaServices' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SpaServices' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.NodeServices" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SpaServices' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.NodeServices" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.SpaServices.Extensions-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SpaServices.Extensions' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SpaServices.Extensions' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.SpaServices" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SpaServices.Extensions' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.SpaServices" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.AspNetCore.TestHost-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.TestHost' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.TestHost' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.6.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.TestHost' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="System.IO.Pipelines" Version="[4.7.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.dotnet-openapi-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.dotnet-openapi' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <!-- Package: Microsoft.DotNet.Web.Client.ItemTemplates-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.DotNet.Web.Client.ItemTemplates' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <!-- Package: Microsoft.DotNet.Web.ItemTemplates-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.DotNet.Web.ItemTemplates' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <!-- Package: Microsoft.DotNet.Web.ProjectTemplates.3.0-->
-  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.DotNet.Web.ProjectTemplates.3.0' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+  <!-- Package: Microsoft.DotNet.Web.ProjectTemplates.3.1-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.DotNet.Web.ProjectTemplates.3.1' ">
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <!-- Package: Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0-->
-  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+  <!-- Package: Microsoft.DotNet.Web.Spa.ProjectTemplates.3.1-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.DotNet.Web.Spa.ProjectTemplates.3.1' ">
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <!-- Package: Microsoft.Extensions.ApiDescription.Client-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.ApiDescription.Client' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <!-- Package: Microsoft.Extensions.ApiDescription.Server-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.ApiDescription.Server' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <!-- Package: Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore' AND '$(TargetFramework)' == 'netstandard2.1' ">
-    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.Extensions.Identity.Core-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Identity.Core' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Identity.Core' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Identity.Core' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Identity.Core' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[3.1.0, )" />
   </ItemGroup>
   <!-- Package: Microsoft.Extensions.Identity.Stores-->
   <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Identity.Stores' ">
-    <BaselinePackageVersion>3.0.0</BaselinePackageVersion>
+    <BaselinePackageVersion>3.1.0</BaselinePackageVersion>
   </PropertyGroup>
-  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Identity.Stores' AND '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Core" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.0.0, )" />
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Identity.Stores' AND '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Core" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.1.0, )" />
   </ItemGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Identity.Stores' AND '$(TargetFramework)' == 'netstandard2.0' ">
-    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Core" Version="[3.0.0, )" />
-    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.0.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Identity.Core" Version="[3.1.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.1.0, )" />
   </ItemGroup>
 </Project>

+ 75 - 77
eng/Baseline.xml

@@ -1,91 +1,89 @@
 <!--
 
-This file contains a list of all the packages and their versions which were released in ASP.NET Core 3.0.0. 
+This file contains a list of all the packages and their versions which were released in ASP.NET Core 3.1.0.
 Update this list when preparing for a new patch.
 
 -->
-
-<Baseline Version="3.0.0">
+<Baseline Version="3.1.0">
   <Package Id="AspNetCoreRuntime.3.0.x64" Version="3.0.0" />
   <Package Id="AspNetCoreRuntime.3.0.x86" Version="3.0.0" />
-  <Package Id="dotnet-sql-cache" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.App.Runtime.win-x64" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.AzureADB2C.UI" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.Certificate" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.Facebook" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.Google" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.Negotiate" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.Twitter" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authentication.WsFederation" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Authorization" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.AzureAppServices.HostingStartup" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.AzureAppServices.SiteExtension" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.AzureAppServicesIntegration" Version="3.0.0" />
+  <Package Id="dotnet-sql-cache" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.App.Runtime.win-x64" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.AzureADB2C.UI" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.Certificate" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.Facebook" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.Google" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.Negotiate" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.Twitter" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authentication.WsFederation" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Authorization" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.AzureAppServices.HostingStartup" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.AzureAppServices.SiteExtension" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.AzureAppServicesIntegration" Version="3.1.0" />
   <Package Id="Microsoft.AspNetCore.Blazor" Version="3.0.0-preview9.19465.2" />
   <Package Id="Microsoft.AspNetCore.Blazor.Build" Version="3.0.0-preview9.19465.2" />
   <Package Id="Microsoft.AspNetCore.Blazor.DevServer" Version="3.0.0-preview9.19465.2" />
   <Package Id="Microsoft.AspNetCore.Blazor.HttpClient" Version="3.0.0-preview9.19465.2" />
   <Package Id="Microsoft.AspNetCore.Blazor.Server" Version="3.0.0-preview9.19465.2" />
   <Package Id="Microsoft.AspNetCore.Blazor.Templates" Version="3.0.0-preview9.19465.2" />
-  <Package Id="Microsoft.AspNetCore.Components" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Components.Analyzers" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Components.Authorization" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Components.Forms" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Components.Web" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.ConcurrencyLimiter" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Connections.Abstractions" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Cryptography.Internal" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.DataProtection" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.DataProtection.Abstractions" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.DataProtection.AzureKeyVault" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.DataProtection.AzureStorage" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.DataProtection.Extensions" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.HeaderPropagation" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Hosting.WindowsServices" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Http.Connections.Client" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Http.Connections.Common" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Http.Features" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Identity.Specification.Tests" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Identity.UI" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.JsonPatch" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Metadata" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.MiddlewareAnalysis" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Mvc.Testing" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.NodeServices" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Owin" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SignalR.Client" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SignalR.Client.Core" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SignalR.Common" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SignalR.Specification.Tests" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SpaServices" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.0.0" />
-  <Package Id="Microsoft.AspNetCore.TestHost" Version="3.0.0" />
-  <Package Id="Microsoft.dotnet-openapi" Version="3.0.0" />
-  <Package Id="Microsoft.DotNet.Web.Client.ItemTemplates" Version="3.0.0" />
-  <Package Id="Microsoft.DotNet.Web.ItemTemplates" Version="3.0.0" />
-  <Package Id="Microsoft.DotNet.Web.ProjectTemplates.3.0" Version="3.0.0" />
-  <Package Id="Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0" Version="3.0.0" />
-  <Package Id="Microsoft.Extensions.ApiDescription.Client" Version="3.0.0" />
-  <Package Id="Microsoft.Extensions.ApiDescription.Server" Version="3.0.0" />
-  <Package Id="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="3.0.0" />
-  <Package Id="Microsoft.Extensions.Identity.Core" Version="3.0.0" />
-  <Package Id="Microsoft.Extensions.Identity.Stores" Version="3.0.0" />
-
+  <Package Id="Microsoft.AspNetCore.Components" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Components.Analyzers" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Components.Authorization" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Components.Forms" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Components.Web" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.ConcurrencyLimiter" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Connections.Abstractions" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Cryptography.Internal" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.Abstractions" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.AzureKeyVault" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.AzureStorage" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.Extensions" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.HeaderPropagation" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Hosting.WindowsServices" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Http.Connections.Client" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Http.Connections.Common" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Http.Features" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Identity.Specification.Tests" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Identity.UI" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.JsonPatch" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Metadata" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.MiddlewareAnalysis" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.NodeServices" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Owin" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SignalR.Client" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SignalR.Client.Core" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SignalR.Common" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SignalR.Specification.Tests" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SpaServices" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.0" />
+  <Package Id="Microsoft.AspNetCore.TestHost" Version="3.1.0" />
+  <Package Id="Microsoft.dotnet-openapi" Version="3.1.0" />
+  <Package Id="Microsoft.DotNet.Web.Client.ItemTemplates" Version="3.1.0" />
+  <Package Id="Microsoft.DotNet.Web.ItemTemplates" Version="3.1.0" />
+  <Package Id="Microsoft.DotNet.Web.ProjectTemplates.3.1" Version="3.1.0" />
+  <Package Id="Microsoft.DotNet.Web.Spa.ProjectTemplates.3.1" Version="3.1.0" />
+  <Package Id="Microsoft.Extensions.ApiDescription.Client" Version="3.1.0" />
+  <Package Id="Microsoft.Extensions.ApiDescription.Server" Version="3.1.0" />
+  <Package Id="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="3.1.0" />
+  <Package Id="Microsoft.Extensions.Identity.Core" Version="3.1.0" />
+  <Package Id="Microsoft.Extensions.Identity.Stores" Version="3.1.0" />
 </Baseline>

+ 1 - 0
eng/Build.props

@@ -34,6 +34,7 @@
                       $(RepoRoot)src\Installers\**\*.*proj;
                       $(RepoRoot)src\SignalR\clients\ts\**\node_modules\**\*.*proj;
                       $(RepoRoot)src\Components\Web.JS\node_modules\**\*.*proj;
+                      $(RepoRoot)src\Components\Blazor\Build\testassets\**\*.*proj;
                       $(RepoRoot)src\Components\Blazor\Templates\src\content\**\*.*proj;
                       $(RepoRoot)src\ProjectTemplates\Web.ProjectTemplates\content\**\*.csproj;
                       $(RepoRoot)src\ProjectTemplates\Web.ProjectTemplates\content\**\*.fsproj;

+ 3 - 2
eng/ProjectReferences.props

@@ -15,6 +15,9 @@
     <ProjectReferenceProvider Include="GetDocument.Insider" ProjectPath="$(RepoRoot)src\Tools\GetDocumentInsider\src\GetDocumentInsider.csproj"  />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Specification.Tests" ProjectPath="$(RepoRoot)src\SignalR\server\Specification.Tests\src\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj"  />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Build" ProjectPath="$(RepoRoot)src\Components\Blazor\Build\src\Microsoft.AspNetCore.Blazor.Build.csproj"  />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.HttpClient" ProjectPath="$(RepoRoot)src\Components\Blazor\Http\src\Microsoft.AspNetCore.Blazor.HttpClient.csproj"  />
+    <ProjectReferenceProvider Include="Mono.WebAssembly.Interop" ProjectPath="$(RepoRoot)src\Components\Blazor\Mono.WebAssembly.Interop\src\Mono.WebAssembly.Interop.csproj"  />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Server" ProjectPath="$(RepoRoot)src\Components\Blazor\Server\src\Microsoft.AspNetCore.Blazor.Server.csproj"  />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" ProjectPath="$(RepoRoot)src\Components\Blazor\Validation\src\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj"  />
     <ProjectReferenceProvider Include="Ignitor" ProjectPath="$(RepoRoot)src\Components\Ignitor\src\Ignitor.csproj"  />
     <ProjectReferenceProvider Include="BlazorServerApp" ProjectPath="$(RepoRoot)src\Components\Samples\BlazorServerApp\BlazorServerApp.csproj"  />
@@ -137,8 +140,6 @@
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" ProjectPath="$(RepoRoot)src\SignalR\server\StackExchangeRedis\src\Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj" RefProjectPath="$(RepoRoot)src\SignalR\server\StackExchangeRedis\ref\Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Authorization" ProjectPath="$(RepoRoot)src\Components\Authorization\src\Microsoft.AspNetCore.Components.Authorization.csproj" RefProjectPath="$(RepoRoot)src\Components\Authorization\ref\Microsoft.AspNetCore.Components.Authorization.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor" ProjectPath="$(RepoRoot)src\Components\Blazor\Blazor\src\Microsoft.AspNetCore.Blazor.csproj" RefProjectPath="$(RepoRoot)src\Components\Blazor\Blazor\ref\Microsoft.AspNetCore.Blazor.csproj" />
-    <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.HttpClient" ProjectPath="$(RepoRoot)src\Components\Blazor\Http\src\Microsoft.AspNetCore.Blazor.HttpClient.csproj" RefProjectPath="$(RepoRoot)src\Components\Blazor\Http\ref\Microsoft.AspNetCore.Blazor.HttpClient.csproj" />
-    <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Server" ProjectPath="$(RepoRoot)src\Components\Blazor\Server\src\Microsoft.AspNetCore.Blazor.Server.csproj" RefProjectPath="$(RepoRoot)src\Components\Blazor\Server\ref\Microsoft.AspNetCore.Blazor.Server.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components" ProjectPath="$(RepoRoot)src\Components\Components\src\Microsoft.AspNetCore.Components.csproj" RefProjectPath="$(RepoRoot)src\Components\Components\ref\Microsoft.AspNetCore.Components.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Forms" ProjectPath="$(RepoRoot)src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj" RefProjectPath="$(RepoRoot)src\Components\Forms\ref\Microsoft.AspNetCore.Components.Forms.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Server" ProjectPath="$(RepoRoot)src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj" RefProjectPath="$(RepoRoot)src\Components\Server\ref\Microsoft.AspNetCore.Components.Server.csproj" />

+ 2 - 2
eng/Version.Details.xml

@@ -9,9 +9,9 @@
 -->
 <Dependencies>
   <ProductDependencies>
-    <Dependency Name="Microsoft.AspNetCore.Blazor.Mono" Version="3.1.0-preview4.19576.1">
+    <Dependency Name="Microsoft.AspNetCore.Blazor.Mono" Version="3.1.0-preview4.19578.1">
       <Uri>https://github.com/aspnet/Blazor</Uri>
-      <Sha>d1f3a3db8a379cc887cd6dde0acf77dcd5e16c36</Sha>
+      <Sha>9ff01af4257317a90b64959fe1c87aef3da4a36f</Sha>
     </Dependency>
     <Dependency Name="Microsoft.AspNetCore.Razor.Language" Version="5.0.0-alpha.1.19603.1">
       <Uri>https://github.com/aspnet/AspNetCore-Tooling</Uri>

+ 1 - 1
eng/Versions.props

@@ -94,7 +94,7 @@
     <!-- Only listed explicitly to workaround https://github.com/dotnet/cli/issues/10528 -->
     <MicrosoftNETCorePlatformsPackageVersion>5.0.0-alpha.1.19556.7</MicrosoftNETCorePlatformsPackageVersion>
     <!-- Packages from aspnet/Blazor -->
-    <MicrosoftAspNetCoreBlazorMonoPackageVersion>3.1.0-preview4.19576.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
+    <MicrosoftAspNetCoreBlazorMonoPackageVersion>3.1.0-preview4.19578.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
     <!-- Packages from aspnet/Extensions -->
     <InternalAspNetCoreAnalyzersPackageVersion>5.0.0-alpha1.19603.1</InternalAspNetCoreAnalyzersPackageVersion>
     <MicrosoftAspNetCoreAnalyzerTestingPackageVersion>5.0.0-alpha1.19603.1</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>

+ 0 - 1
eng/tools/BaselineGenerator/BaselineGenerator.csproj

@@ -3,7 +3,6 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
-    <StartArguments>-s https://api.nuget.org/v3/index.json</StartArguments>
     <StartWorkingDirectory>$(MSBuildThisFileDirectory)../../</StartWorkingDirectory>
   </PropertyGroup>
 

+ 3 - 3
src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.csproj

@@ -1,10 +1,10 @@
 <!-- This file is automatically generated. -->
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFrameworks>netstandard2.0</TargetFrameworks>
+    <TargetFrameworks>netstandard2.1</TargetFrameworks>
   </PropertyGroup>
-  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
-    <Compile Include="Microsoft.AspNetCore.Blazor.netstandard2.0.cs" />
+  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
+    <Compile Include="Microsoft.AspNetCore.Blazor.netstandard2.1.cs" />
     <Reference Include="Mono.WebAssembly.Interop"  />
     <Reference Include="Microsoft.AspNetCore.Components.Web"  />
     <Reference Include="Microsoft.Extensions.Options"  />

+ 0 - 0
src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.netstandard2.0.cs → src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.netstandard2.1.cs


+ 1 - 1
src/Components/Blazor/Blazor/src/Microsoft.AspNetCore.Blazor.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <Description>Build client-side single-page applications (SPAs) with Blazor running under WebAssembly.</Description>
     <IsShippingPackage>true</IsShippingPackage>
   </PropertyGroup>

+ 0 - 61
src/Components/Blazor/Build/src/Cli/Commands/ResolveRuntimeDependenciesCommand.cs

@@ -1,61 +0,0 @@
-// 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.IO;
-using Microsoft.Extensions.CommandLineUtils;
-
-namespace Microsoft.AspNetCore.Blazor.Build.DevServer.Commands
-{
-    class ResolveRuntimeDependenciesCommand
-    {
-        public static void Command(CommandLineApplication command)
-        {
-            var referencesFile = command.Option("--references",
-                "The path to a file that lists the paths to given referenced dll files",
-                CommandOptionType.SingleValue);
-
-            var baseClassLibrary = command.Option("--base-class-library",
-                "Full path to a directory in which BCL assemblies can be found",
-                CommandOptionType.MultipleValue);
-
-            var outputPath = command.Option("--output",
-                "Path to the output file that will contain the list with the full paths of the resolved assemblies",
-                CommandOptionType.SingleValue);
-
-            var mainAssemblyPath = command.Argument("assembly",
-                "Path to the assembly containing the entry point of the application.");
-
-            command.OnExecute(() =>
-            {
-                if (string.IsNullOrEmpty(mainAssemblyPath.Value) ||
-                    !baseClassLibrary.HasValue() || !outputPath.HasValue())
-                {
-                    command.ShowHelp(command.Name);
-                    return 1;
-                }
-
-                try
-                {
-                    var referencesSources = referencesFile.HasValue()
-                        ? File.ReadAllLines(referencesFile.Value())
-                        : Array.Empty<string>();
-
-                    RuntimeDependenciesResolver.ResolveRuntimeDependencies(
-                        mainAssemblyPath.Value,
-                        referencesSources,
-                        baseClassLibrary.Values.ToArray(),
-                        outputPath.Value());
-
-                    return 0;
-                }
-                catch (Exception ex)
-                {
-                    Console.WriteLine($"ERROR: {ex.Message}");
-                    Console.WriteLine(ex.StackTrace);
-                    return 1;
-                }
-            });
-        }
-    }
-}

+ 0 - 68
src/Components/Blazor/Build/src/Cli/Commands/WriteBootJsonCommand.cs

@@ -1,68 +0,0 @@
-// 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 Microsoft.Extensions.CommandLineUtils;
-using System;
-using System.IO;
-
-namespace Microsoft.AspNetCore.Blazor.Build.DevServer.Commands
-{
-    internal class WriteBootJsonCommand
-    {
-        public static void Command(CommandLineApplication command)
-        {
-            var referencesFile = command.Option("--references",
-                "The path to a file that lists the paths to given referenced dll files",
-                CommandOptionType.SingleValue);
-
-            var embeddedResourcesFile = command.Option("--embedded-resources",
-                "The path to a file that lists the paths of .NET assemblies that may contain embedded resources (typically, referenced assemblies in their pre-linked states)",
-                CommandOptionType.SingleValue);
-
-            var outputPath = command.Option("--output",
-                "Path to the output file",
-                CommandOptionType.SingleValue);
-
-            var mainAssemblyPath = command.Argument("assembly",
-                "Path to the assembly containing the entry point of the application.");
-
-            var linkerEnabledFlag = command.Option("--linker-enabled",
-                "If set, specifies that the application is being built with linking enabled.",
-                CommandOptionType.NoValue);
-
-            command.OnExecute(() =>
-            {
-                if (string.IsNullOrEmpty(mainAssemblyPath.Value) || !outputPath.HasValue())
-                {
-                    command.ShowHelp(command.Name);
-                    return 1;
-                }
-
-                try
-                {
-                    var referencesSources = referencesFile.HasValue()
-                        ? File.ReadAllLines(referencesFile.Value())
-                        : Array.Empty<string>();
-
-                    var embeddedResourcesSources = embeddedResourcesFile.HasValue()
-                        ? File.ReadAllLines(embeddedResourcesFile.Value())
-                        : Array.Empty<string>();
-
-                    BootJsonWriter.WriteFile(
-                        mainAssemblyPath.Value,
-                        referencesSources,
-                        embeddedResourcesSources,
-                        linkerEnabledFlag.HasValue(),
-                        outputPath.Value());
-                    return 0;
-                }
-                catch (Exception ex)
-                {
-                    Console.WriteLine($"ERROR: {ex.Message}");
-                    Console.WriteLine(ex.StackTrace);
-                    return 1;
-                }
-            });
-        }
-    }
-}

+ 0 - 33
src/Components/Blazor/Build/src/Cli/Program.cs

@@ -1,33 +0,0 @@
-// 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 Microsoft.AspNetCore.Blazor.Build.DevServer.Commands;
-using Microsoft.Extensions.CommandLineUtils;
-
-namespace Microsoft.AspNetCore.Blazor.Build
-{
-    static class Program
-    {
-        static int Main(string[] args)
-        {
-            var app = new CommandLineApplication
-            {
-                Name = "Microsoft.AspNetCore.Blazor.Build"
-            };
-            app.HelpOption("-?|-h|--help");
-
-            app.Command("resolve-dependencies", ResolveRuntimeDependenciesCommand.Command);
-            app.Command("write-boot-json", WriteBootJsonCommand.Command);
-
-            if (args.Length > 0)
-            {
-                return app.Execute(args);
-            }
-            else
-            {
-                app.ShowHelp();
-                return 0;
-            }
-        }
-    }
-}

+ 0 - 95
src/Components/Blazor/Build/src/Core/BootJsonWriter.cs

@@ -1,95 +0,0 @@
-// 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.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.Json;
-using Microsoft.AspNetCore.Components;
-using Mono.Cecil;
-
-namespace Microsoft.AspNetCore.Blazor.Build
-{
-    internal class BootJsonWriter
-    {
-        public static void WriteFile(
-            string assemblyPath,
-            string[] assemblyReferences,
-            string[] embeddedResourcesSources,
-            bool linkerEnabled,
-            string outputPath)
-        {
-            var embeddedContent = EmbeddedResourcesProcessor.ExtractEmbeddedResources(
-                embeddedResourcesSources, Path.GetDirectoryName(outputPath));
-            var bootJsonText = GetBootJsonContent(
-                Path.GetFileName(assemblyPath),
-                GetAssemblyEntryPoint(assemblyPath),
-                assemblyReferences,
-                embeddedContent,
-                linkerEnabled);
-            var normalizedOutputPath = Path.GetFullPath(outputPath);
-            Console.WriteLine("Writing boot data to: " + normalizedOutputPath);
-            File.WriteAllText(normalizedOutputPath, bootJsonText);
-        }
-
-        public static string GetBootJsonContent(string assemblyFileName, string entryPoint, string[] assemblyReferences, IEnumerable<EmbeddedResourceInfo> embeddedContent, bool linkerEnabled)
-        {
-            var data = new BootJsonData(
-                assemblyFileName,
-                entryPoint,
-                assemblyReferences,
-                embeddedContent,
-                linkerEnabled);
-            return JsonSerializer.Serialize(data, JsonSerializerOptionsProvider.Options);
-        }
-
-        private static string GetAssemblyEntryPoint(string assemblyPath)
-        {
-            using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath))
-            {
-                var entryPoint = assemblyDefinition.EntryPoint;
-                if (entryPoint == null)
-                {
-                    throw new ArgumentException($"The assembly at {assemblyPath} has no specified entry point.");
-                }
-
-                return $"{entryPoint.DeclaringType.FullName}::{entryPoint.Name}";
-            }
-        }
-
-        /// <summary>
-        /// Defines the structure of a Blazor boot JSON file
-        /// </summary>
-        class BootJsonData
-        {
-            public string Main { get; }
-            public string EntryPoint { get; }
-            public IEnumerable<string> AssemblyReferences { get; }
-            public IEnumerable<string> CssReferences { get; }
-            public IEnumerable<string> JsReferences { get; }
-            public bool LinkerEnabled { get; }
-
-            public BootJsonData(
-                string entrypointAssemblyWithExtension,
-                string entryPoint,
-                IEnumerable<string> assemblyReferences,
-                IEnumerable<EmbeddedResourceInfo> embeddedContent,
-                bool linkerEnabled)
-            {
-                Main = entrypointAssemblyWithExtension;
-                EntryPoint = entryPoint;
-                AssemblyReferences = assemblyReferences;
-                LinkerEnabled = linkerEnabled;
-
-                CssReferences = embeddedContent
-                    .Where(c => c.Kind == EmbeddedResourceKind.Css)
-                    .Select(c => c.RelativePath);
-
-                JsReferences = embeddedContent
-                    .Where(c => c.Kind == EmbeddedResourceKind.JavaScript)
-                    .Select(c => c.RelativePath);
-            }
-        }
-    }
-}

+ 0 - 17
src/Components/Blazor/Build/src/Core/EmbeddedResources/EmbeddedResourceInfo.cs

@@ -1,17 +0,0 @@
-// 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.
-
-namespace Microsoft.AspNetCore.Blazor.Build
-{
-    internal class EmbeddedResourceInfo
-    {
-        public EmbeddedResourceKind Kind { get; }
-        public string RelativePath { get; }
-
-        public EmbeddedResourceInfo(EmbeddedResourceKind kind, string relativePath)
-        {
-            Kind = kind;
-            RelativePath = relativePath;
-        }
-    }
-}

+ 0 - 12
src/Components/Blazor/Build/src/Core/EmbeddedResources/EmbeddedResourceKind.cs

@@ -1,12 +0,0 @@
-// 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.
-
-namespace Microsoft.AspNetCore.Blazor.Build
-{
-    internal enum EmbeddedResourceKind
-    {
-        JavaScript,
-        Css,
-        Static
-    }
-}

+ 0 - 137
src/Components/Blazor/Build/src/Core/EmbeddedResources/EmbeddedResourcesProcessor.cs

@@ -1,137 +0,0 @@
-// 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 Mono.Cecil;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace Microsoft.AspNetCore.Blazor.Build
-{
-    internal class EmbeddedResourcesProcessor
-    {
-        const string ContentSubdirName = "_content";
-
-        private readonly static Dictionary<string, EmbeddedResourceKind> _knownResourceKindsByNamePrefix = new Dictionary<string, EmbeddedResourceKind>
-        {
-            { "blazor:js:", EmbeddedResourceKind.JavaScript },
-            { "blazor:css:", EmbeddedResourceKind.Css },
-            { "blazor:file:", EmbeddedResourceKind.Static },
-        };
-
-        /// <summary>
-        /// Finds Blazor-specific embedded resources in the specified assemblies, writes them
-        /// to disk, and returns a description of those resources in dependency order.
-        /// </summary>
-        /// <param name="referencedAssemblyPaths">The paths to assemblies that may contain embedded resources.</param>
-        /// <param name="outputDir">The path to the directory where output is being written.</param>
-        /// <returns>A description of the embedded resources that were written to disk.</returns>
-        public static IReadOnlyList<EmbeddedResourceInfo> ExtractEmbeddedResources(
-            IEnumerable<string> referencedAssemblyPaths, string outputDir)
-        {
-            // Clean away any earlier state
-            var contentDir = Path.Combine(outputDir, ContentSubdirName);
-            if (Directory.Exists(contentDir))
-            {
-                Directory.Delete(contentDir, recursive: true);
-            }
-
-            // First, get an ordered list of AssemblyDefinition instances
-            var referencedAssemblyDefinitions = referencedAssemblyPaths
-                .Where(path => !Path.GetFileName(path).StartsWith("System.", StringComparison.Ordinal)) // Skip System.* because they are never going to contain embedded resources that we want
-                .Select(path => AssemblyDefinition.ReadAssembly(path))
-                .ToList();
-            referencedAssemblyDefinitions.Sort(OrderWithReferenceSubjectFirst);
-
-            // Now process them in turn
-            return referencedAssemblyDefinitions
-                .SelectMany(def => ExtractEmbeddedResourcesFromSingleAssembly(def, outputDir))
-                .ToList()
-                .AsReadOnly();
-        }
-
-        private static IEnumerable<EmbeddedResourceInfo> ExtractEmbeddedResourcesFromSingleAssembly(
-            AssemblyDefinition assemblyDefinition, string outputDirPath)
-        {
-            var assemblyName = assemblyDefinition.Name.Name;
-            foreach (var res in assemblyDefinition.MainModule.Resources)
-            {
-                if (TryExtractEmbeddedResource(assemblyName, res, outputDirPath, out var extractedResourceInfo))
-                {
-                    yield return extractedResourceInfo;
-                }
-            }
-        }
-
-        private static bool TryExtractEmbeddedResource(string assemblyName, Resource resource, string outputDirPath, out EmbeddedResourceInfo extractedResourceInfo)
-        {
-            if (resource is EmbeddedResource embeddedResource)
-            {
-                if (TryInterpretLogicalName(resource.Name, out var kind, out var name))
-                {
-                    // Prefix the output path with the assembly name to ensure no clashes
-                    // Also be invariant to the OS on which the package was built
-                    name = Path.Combine(ContentSubdirName, assemblyName, EnsureHasPathSeparators(name, Path.DirectorySeparatorChar));
-
-                    // Write the file content to disk, ensuring we don't try to write outside the output root
-                    var outputPath = Path.GetFullPath(Path.Combine(outputDirPath, name));
-                    if (!outputPath.StartsWith(outputDirPath))
-                    {
-                        throw new InvalidOperationException($"Cannot write embedded resource from assembly '{assemblyName}' to '{outputPath}' because it is outside the expected directory {outputDirPath}");
-                    }
-                    WriteResourceFile(embeddedResource, outputPath);
-
-                    // The URLs we write into the boot json file need to use web-style directory separators
-                    extractedResourceInfo = new EmbeddedResourceInfo(kind, EnsureHasPathSeparators(name, '/'));
-                    return true;
-                }
-            }
-
-            extractedResourceInfo = null;
-            return false;
-        }
-
-        private static void WriteResourceFile(EmbeddedResource resource, string outputPath)
-        {
-            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
-            using (var outputStream = File.OpenWrite(outputPath))
-            {
-                resource.GetResourceStream().CopyTo(outputStream);
-            }
-        }
-
-        private static string EnsureHasPathSeparators(string name, char desiredSeparatorChar) => name
-            .Replace('\\', desiredSeparatorChar)
-            .Replace('/', desiredSeparatorChar);
-
-        private static bool TryInterpretLogicalName(string logicalName, out EmbeddedResourceKind kind, out string resolvedName)
-        {
-            foreach (var kvp in _knownResourceKindsByNamePrefix)
-            {
-                if (logicalName.StartsWith(kvp.Key, StringComparison.Ordinal))
-                {
-                    kind = kvp.Value;
-                    resolvedName = logicalName.Substring(kvp.Key.Length);
-                    return true;
-                }
-            }
-
-            kind = default;
-            resolvedName = default;
-            return false;
-        }
-
-        // For each assembly B that references A, we want the resources from A to be loaded before
-        // the references for B (because B's resources might depend on A's resources)
-        private static int OrderWithReferenceSubjectFirst(AssemblyDefinition a, AssemblyDefinition b)
-            => AssemblyHasReference(a, b) ? 1
-            : AssemblyHasReference(b, a) ? -1
-            : 0;
-
-        private static bool AssemblyHasReference(AssemblyDefinition from, AssemblyDefinition to)
-            => from.MainModule.AssemblyReferences
-                .Select(reference => reference.Name)
-                .Contains(to.Name.Name, StringComparer.Ordinal);
-    }
-}

+ 0 - 165
src/Components/Blazor/Build/src/Core/RuntimeDependenciesResolver.cs

@@ -1,165 +0,0 @@
-// 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.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using Mono.Cecil;
-
-namespace Microsoft.AspNetCore.Blazor.Build
-{
-    internal class RuntimeDependenciesResolver
-    {
-        public static void ResolveRuntimeDependencies(
-            string entryPoint,
-            string[] applicationDependencies,
-            string[] monoBclDirectories,
-            string outputFile)
-        {
-            var paths = ResolveRuntimeDependenciesCore(entryPoint, applicationDependencies, monoBclDirectories);
-            File.WriteAllLines(outputFile, paths);
-        }
-
-        public static IEnumerable<string> ResolveRuntimeDependenciesCore(
-            string entryPoint,
-            string[] applicationDependencies,
-            string[] monoBclDirectories)
-        {
-            var assembly = new AssemblyEntry(entryPoint, AssemblyDefinition.ReadAssembly(entryPoint));
-
-            var dependencies = applicationDependencies
-                .Select(a => new AssemblyEntry(a, AssemblyDefinition.ReadAssembly(a)))
-                .ToArray();
-
-            var bcl = monoBclDirectories
-                .SelectMany(d => Directory.EnumerateFiles(d, "*.dll").Select(f => Path.Combine(d, f)))
-                .Select(a => new AssemblyEntry(a, AssemblyDefinition.ReadAssembly(a)))
-                .ToArray();
-
-            var assemblyResolutionContext = new AssemblyResolutionContext(
-                assembly,
-                dependencies,
-                bcl);
-
-            assemblyResolutionContext.ResolveAssemblies();
-
-            var paths = assemblyResolutionContext.Results.Select(r => r.Path);
-            return paths.Concat(FindPdbs(paths));
-        }
-
-        private static IEnumerable<string> FindPdbs(IEnumerable<string> dllPaths)
-        {
-            return dllPaths
-                .Select(path => Path.ChangeExtension(path, "pdb"))
-                .Where(path => File.Exists(path));
-        }
-
-        public class AssemblyResolutionContext
-        {
-            public AssemblyResolutionContext(
-                AssemblyEntry assembly,
-                AssemblyEntry[] dependencies,
-                AssemblyEntry[] bcl)
-            {
-                Assembly = assembly;
-                Dependencies = dependencies;
-                Bcl = bcl;
-            }
-
-            public AssemblyEntry Assembly { get; }
-            public AssemblyEntry[] Dependencies { get; }
-            public AssemblyEntry[] Bcl { get; }
-
-            public IList<AssemblyEntry> Results { get; } = new List<AssemblyEntry>();
-
-            internal void ResolveAssemblies()
-            {
-                var visitedAssemblies = new HashSet<string>();
-                var pendingAssemblies = new Stack<AssemblyNameReference>();
-                pendingAssemblies.Push(Assembly.Definition.Name);
-                ResolveAssembliesCore();
-
-                void ResolveAssembliesCore()
-                {
-                    while (pendingAssemblies.TryPop(out var current))
-                    {
-                        if (!visitedAssemblies.Contains(current.Name))
-                        {
-                            visitedAssemblies.Add(current.Name);
-
-                            // Not all references will be resolvable within the Mono BCL, particularly
-                            // when building for server-side Blazor as you will be running on CoreCLR
-                            // and therefore may depend on System.* BCL assemblies that aren't present
-                            // in Mono WebAssembly. Skipping unresolved assemblies here is equivalent
-                            // to passing "--skip-unresolved true" to the Mono linker.
-                            var resolved = Resolve(current);
-                            if (resolved != null)
-                            {
-                                Results.Add(resolved);
-                                var references = GetAssemblyReferences(resolved);
-                                foreach (var reference in references)
-                                {
-                                    pendingAssemblies.Push(reference);
-                                }
-                            }
-                        }
-                    }
-                }
-
-                IEnumerable<AssemblyNameReference> GetAssemblyReferences(AssemblyEntry current) =>
-                    current.Definition.Modules.SelectMany(m => m.AssemblyReferences);
-
-                AssemblyEntry Resolve(AssemblyNameReference current)
-                {
-                    if (Assembly.Definition.Name.Name == current.Name)
-                    {
-                        return Assembly;
-                    }
-
-                    var referencedAssemblyCandidate = FindCandidate(current, Dependencies);
-                    var bclAssemblyCandidate = FindCandidate(current, Bcl);
-
-                    // Resolution logic. For right now, we will prefer the mono BCL version of a given
-                    // assembly if there is a candidate assembly and an equivalent mono assembly.
-                    if (bclAssemblyCandidate != null)
-                    {
-                        return bclAssemblyCandidate;
-                    }
-
-                    return referencedAssemblyCandidate;
-                }
-
-                AssemblyEntry FindCandidate(AssemblyNameReference current, AssemblyEntry[] candidates)
-                {
-                    // Do simple name match. Assume no duplicates.
-                    foreach (var candidate in candidates)
-                    {
-                        if (current.Name == candidate.Definition.Name.Name)
-                        {
-                            return candidate;
-                        }
-                    }
-
-                    return null;
-                }
-            }
-        }
-
-        [DebuggerDisplay("{ToString(),nq}")]
-        public class AssemblyEntry
-        {
-            public AssemblyEntry(string path, AssemblyDefinition definition)
-            {
-                Path = path;
-                Definition = definition;
-            }
-
-            public string Path { get; set; }
-            public AssemblyDefinition Definition { get; set; }
-
-            public override string ToString() => Definition.FullName;
-        }
-    }
-}

+ 45 - 15
src/Components/Blazor/Build/src/Microsoft.AspNetCore.Blazor.Build.csproj

@@ -1,41 +1,71 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+    <TargetFrameworks>$(DefaultNetCoreTargetFramework);net46</TargetFrameworks>
+    <TargetName>Microsoft.AspNetCore.Blazor.Build.Tasks</TargetName>
+    <AssemblyName>Microsoft.AspNetCore.Blazor.Build</AssemblyName>
     <Description>Build mechanism for ASP.NET Core Blazor applications.</Description>
-    <OutputType>Exe</OutputType>
     <IsShippingPackage>true</IsShippingPackage>
     <HasReferenceAssembly>false</HasReferenceAssembly>
+    <GenerateDependencyFile>false</GenerateDependencyFile>
   </PropertyGroup>
 
   <!-- Pack settings -->
   <PropertyGroup>
     <!-- Producing this package requires building with NodeJS enabled. -->
     <IsPackable Condition="'$(BuildNodeJS)' == 'false'">false</IsPackable>
-    <GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);Publish</GenerateNuspecDependsOn>
     <NoPackageAnalysis>true</NoPackageAnalysis>
     <NuspecFile>Microsoft.AspNetCore.Blazor.Build.nuspec</NuspecFile>
   </PropertyGroup>
 
   <ItemGroup>
     <NuspecProperty Include="configuration=$(Configuration)" />
-    <NuspecProperty Include="publishDir=$(PublishDir)" />
+    <NuspecProperty Include="taskskDir=$(OutputPath)tools" />
     <NuspecProperty Include="componentsversion=$(ComponentsPackageVersion)" />
     <NuspecProperty Include="razorversion=$(MicrosoftAspNetCoreRazorDesignPackageVersion)" />
     <NuspecProperty Include="blazormonoversion=$(MicrosoftAspNetCoreBlazorMonoPackageVersion)" />
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Condition="'$(BuildNodeJS)' != 'false' and '$(BuildingInsideVisualStudio)' != 'true'" Include="$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj" ReferenceOutputAssembly="false" />
-    <Reference Include="Microsoft.AspNetCore.Components" />
-    <Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
-    <Reference Include="Microsoft.Extensions.FileProviders.Composite" />
-    <Reference Include="Microsoft.Extensions.FileProviders.Physical" />
-    <Reference Include="Mono.Cecil" />
-    <Reference Include="System.CodeDom" />
-  </ItemGroup>
+    <!-- Add a project dependency without reference output assemblies to enforce build order -->
+    <!-- Applying workaround for https://github.com/microsoft/msbuild/issues/2661 and https://github.com/dotnet/sdk/issues/952 -->
+    <ProjectReference
+      Include="$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj"
+      ReferenceOutputAssemblies="false"
+      SkipGetTargetFrameworkProperties="true"
+      UndefineProperties="TargetFramework"
+      Private="false"
+      Condition="'$(BuildNodeJS)' != 'false' and '$(BuildingInsideVisualStudio)' != 'true'" />
 
-  <ItemGroup>
-    <Compile Include="..\..\..\Shared\src\JsonSerializerOptionsProvider.cs" />
+    <Reference Include="Microsoft.Build.Framework" ExcludeAssets="Runtime" />
+    <Reference Include="Microsoft.Build.Utilities.Core"  ExcludeAssets="Runtime" />
+    <Reference Include="System.Reflection.Metadata" Condition="'$(TargetFramework)' == 'net46'" />
   </ItemGroup>
+
+  <Target Name="CopyBuildTask" BeforeTargets="Build" Condition="'$(DotNetBuildFromSource)' != 'true' AND '$(IsInnerBuild)' != 'true'">
+      <!--
+      The task produced by this project is referenced within this solution. When building, Visual Studio will lock up the assembly.
+      Any attempts to overwrite the binary with a newer version will fail. This is particularly grating if a developer "Rebuilds" the project
+      after an initial build since that would always attempt to overwrite the tasks dll
+
+      This target attempts to make this solution more usable at the cost of a more onerous inner-loop build of the Blazor.Build tasks.
+      We'll copy the tasks to a location other that than the build output and use that in the Blazor.Build.targets. In the most common
+      case where these tasks aren't being worked on, everything should work great. However, if you're attemping to modify these tasks,
+      you will need to manaully stop MSBuild.exe processes
+      -->
+
+    <ItemGroup>
+      <_NetCoreFilesToCopy Include="$(OutputPath)$(DefaultNetCoreTargetFramework)\*" TargetPath="netcoreapp\" />
+      <_DesktopFilesToCopy Include="$(OutputPath)net46\*" TargetPath="netfx\" />
+      <_AllFilesToCopy Include="@(_NetCoreFilesToCopy);@(_DesktopFilesToCopy)" />
+    </ItemGroup>
+
+    <Error Text="No files found in $(OutputPath)$(DefaultNetCoreTargetFramework)" Condition="@(_NetCoreFilesToCopy->Count()) == 0" />
+    <Error Text="No files found in $(OutputPath)net46" Condition="@(_DesktopFilesToCopy->Count()) == 0" />
+
+    <Copy SourceFiles="@(_AllFilesToCopy)" DestinationFiles="@(_AllFilesToCopy->'$(OutputPath)tools\%(TargetPath)%(FileName)%(Extension)')" SkipUnchangedFiles="true" Retries="1" ContinueOnError="true">
+      <Output TaskParameter="CopiedFiles" ItemName="FileWrites" />
+    </Copy>
+  </Target>
+
 </Project>

+ 2 - 2
src/Components/Blazor/Build/src/Microsoft.AspNetCore.Blazor.Build.nuspec

@@ -11,7 +11,7 @@
     <file src="..\..\..\THIRD-PARTY-NOTICES.txt" />
     <file src="build\**" target="build" />
     <file src="targets\**" target="targets" />
-    <file src="$publishdir$**\*" target="tools/" />
-    <file src="..\..\..\Web.JS\dist\$configuration$\blazor.*.js" target="tools/blazor" />
+    <file src="$taskskDir$\**" target="tools/" />
+    <file src="..\..\..\Web.JS\dist\$configuration$\blazor.webassembly.js" target="tools/blazor" />
   </files>
 </package>

+ 25 - 0
src/Components/Blazor/Build/src/ReferenceBlazorBuildFromSource.props

@@ -0,0 +1,25 @@
+<Project>
+  <!--
+  Importing this file is equivalent to having:
+    <PackageDependency Include="Microsoft.AspNetCore.Blazor.Build" />
+  ... except it's much more convenient when working in this repo, because it consumes the
+  Blazor.Build targets/exe directly without needing this project to be packed into a .nupkg.
+
+  This is only intended for use by other projects in this repo.
+  -->
+
+  <PropertyGroup>
+    <ComponentsRoot Condition="'$(ComponentsRoot)'==''">$(MSBuildThisFileDirectory)..\..\..\</ComponentsRoot>
+    <BlazorJsPath>$(ComponentsRoot)Web.JS\dist\$(Configuration)\blazor.webassembly.js</BlazorJsPath>
+    <BlazorJsMapPath>$(ComponentsRoot)Web.JS\dist\$(Configuration)\blazor.webassembly.js.map</BlazorJsMapPath>
+    <BlazorToolsDir>$(MSBuildThisFileDirectory)bin\$(Configuration)\tools\</BlazorToolsDir>
+  </PropertyGroup>
+
+  <Target Name="CheckBlazorJSFiles" BeforeTargets="Build">
+    <Error Text="blazor.webassembly.js file could not be found at $(BlazorJsPath)" Condition="!Exists($(BlazorJsPath))" />
+  </Target>
+
+  <Import Project="$(MSBuildThisFileDirectory)targets/All.props" />
+  <Import Project="$(MSBuildThisFileDirectory)targets/All.targets" />
+
+</Project>

+ 10 - 18
src/Components/Blazor/Build/src/ReferenceFromSource.props

@@ -1,21 +1,6 @@
 <Project>
 
-  <!--
-  Importing this file is equivalent to having:
-    <PackageDependency Include="Microsoft.AspNetCore.Blazor.Build" />
-  ... except it's much more convenient when working in this repo, because it consumes the
-  Blazor.Build targets/exe directly without needing this project to be packed into a .nupkg.
-
-  This is only intended for use by other projects in this repo.
-  -->
-
-  <PropertyGroup>
-    <BlazorBuildReferenceFromSource>true</BlazorBuildReferenceFromSource>
-    <BlazorJsPath>$(RepoRoot)src\Components\Web.JS\dist\$(Configuration)\blazor.*.js.*</BlazorJsPath>
-  </PropertyGroup>
-
-  <Import Project="$(MSBuildThisFileDirectory)targets/All.props" />
-  <Import Project="$(MSBuildThisFileDirectory)targets/All.targets" />
+  <Import Project="ReferenceBlazorBuildFromSource.props" />
 
   <!--
     Debugging support using blazor-devserver serve.
@@ -35,6 +20,14 @@
     <Reference Include="Microsoft.AspNetCore.Blazor.Mono" />
   </ItemGroup>
 
+  <Target Name="_BuildBlazorBuildProject" BeforeTargets="ResolveProjectReferences">
+    <!--
+      The Blazor.Build project cross-compiles and we need the output of all TFMs to be available in the build output.
+      We can't represent this in any good way using ProjectReference elements, but we can try and build it instead.
+    -->
+    <MSBuild Projects="$(MSBuildThisFileDirectory)Microsoft.AspNetCore.Blazor.Build.csproj" />
+  </Target>
+
   <!-- This is used as a P2P when building the repo. Normal Blazor projects will get this as a reference through the Blazor.Build package -->
   <ItemGroup>
     <!-- Ensures these projects are built before the consuming project, but without
@@ -42,9 +35,8 @@
          given that the packed version of this project wouldn't add a .dll reference) -->
     <ProjectReference Include="$(MSBuildThisFileDirectory)Microsoft.AspNetCore.Blazor.Build.csproj">
       <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
-      <!-- Optimization. Do not require framework compatibility between these projects. -->
       <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
-      <UndefineProperties>TargetFramework</UndefineProperties>
+      <Properties>TargetFramework=$(DefaultNetCoreTargetFramework)</Properties>
     </ProjectReference>
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\DevServer\src\Microsoft.AspNetCore.Blazor.DevServer.csproj">
       <ReferenceOutputAssembly>false</ReferenceOutputAssembly>

+ 56 - 0
src/Components/Blazor/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs

@@ -0,0 +1,56 @@
+// 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.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    // Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/CreateRootDescriptorFile.cs
+    public class BlazorCreateRootDescriptorFile : Task
+    {
+        [Required]
+        public ITaskItem[] AssemblyNames { get; set; }
+
+        [Required]
+        public ITaskItem RootDescriptorFilePath { get; set; }
+
+        public override bool Execute()
+        {
+            using var fileStream = File.Create(RootDescriptorFilePath.ItemSpec);
+            var assemblyNames = AssemblyNames.Select(a => a.ItemSpec);
+
+            WriteRootDescriptor(fileStream, assemblyNames);
+            return true;
+        }
+
+        internal static void WriteRootDescriptor(Stream stream, IEnumerable<string> assemblyNames)
+        {
+            var roots = new XElement("linker");
+            foreach (var assemblyName in assemblyNames)
+            {
+                roots.Add(new XElement("assembly",
+                    new XAttribute("fullname", assemblyName),
+                    new XElement("type",
+                        new XAttribute("fullname", "*"),
+                        new XAttribute("required", "true"))));
+            }
+
+            var xmlWriterSettings = new XmlWriterSettings
+            {
+                Indent = true,
+                OmitXmlDeclaration = true
+            };
+
+            using var writer = XmlWriter.Create(stream, xmlWriterSettings);
+            var xDocument = new XDocument(roots);
+
+            xDocument.Save(writer);
+        }
+    }
+}

+ 189 - 0
src/Components/Blazor/Build/src/Tasks/BlazorILLink.cs

@@ -0,0 +1,189 @@
+// 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.Collections.Generic;
+using System.IO;
+using System.Text;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Blazor.Build.Tasks
+{
+    // Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/LinkTask.cs
+    public class BlazorILLink : ToolTask
+    {
+        private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH";
+
+        [Required]
+        public string ILLinkPath { get; set; }
+
+        [Required]
+        public ITaskItem[] AssemblyPaths { get; set; }
+
+        public ITaskItem[] ReferenceAssemblyPaths { get; set; }
+
+        [Required]
+        public ITaskItem[] RootAssemblyNames { get; set; }
+
+        [Required]
+        public ITaskItem OutputDirectory { get; set; }
+
+        public ITaskItem[] RootDescriptorFiles { get; set; }
+
+        public bool ClearInitLocals { get; set; }
+
+        public string ClearInitLocalsAssemblies { get; set; }
+
+        public string ExtraArgs { get; set; }
+
+        public bool DumpDependencies { get; set; }
+
+        private string _dotnetPath;
+
+        private string DotNetPath
+        {
+            get
+            {
+                if (!string.IsNullOrEmpty(_dotnetPath))
+                {
+                    return _dotnetPath;
+                }
+
+                _dotnetPath = Environment.GetEnvironmentVariable(DotNetHostPathEnvironmentName);
+                if (string.IsNullOrEmpty(_dotnetPath))
+                {
+                    throw new InvalidOperationException($"{DotNetHostPathEnvironmentName} is not set");
+                }
+
+                return _dotnetPath;
+            }
+        }
+
+        protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High;
+
+        protected override string ToolName => Path.GetFileName(DotNetPath);
+
+        protected override string GenerateFullPathToTool() => DotNetPath;
+
+        protected override string GenerateCommandLineCommands() => ILLinkPath;
+
+        private static string Quote(string path)
+        {
+            return $"\"{path.TrimEnd('\\')}\"";
+        }
+
+        protected override string GenerateResponseFileCommands()
+        {
+            var args = new StringBuilder();
+
+            if (RootDescriptorFiles != null)
+            {
+                foreach (var rootFile in RootDescriptorFiles)
+                {
+                    args.Append("-x ").AppendLine(Quote(rootFile.ItemSpec));
+                }
+            }
+
+            foreach (var assemblyItem in RootAssemblyNames)
+            {
+                args.Append("-a ").AppendLine(Quote(assemblyItem.ItemSpec));
+            }
+
+            var assemblyNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+            foreach (var assembly in AssemblyPaths)
+            {
+                var assemblyPath = assembly.ItemSpec;
+                var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
+
+                // If there are multiple paths with the same assembly name, only use the first one.
+                if (!assemblyNames.Add(assemblyName))
+                {
+                    continue;
+                }
+
+                args.Append("-reference ")
+                    .AppendLine(Quote(assemblyPath));
+
+                var action = assembly.GetMetadata("action");
+                if ((action != null) && (action.Length > 0))
+                {
+                    args.Append("-p ");
+                    args.Append(action);
+                    args.Append(" ").AppendLine(Quote(assemblyName));
+                }
+            }
+
+            if (ReferenceAssemblyPaths != null)
+            {
+                foreach (var assembly in ReferenceAssemblyPaths)
+                {
+                    var assemblyPath = assembly.ItemSpec;
+                    var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
+
+                    // Don't process references for which we already have
+                    // implementation assemblies.
+                    if (assemblyNames.Contains(assemblyName))
+                    {
+                        continue;
+                    }
+
+                    args.Append("-reference ").AppendLine(Quote(assemblyPath));
+
+                    // Treat reference assemblies as "skip". Ideally we
+                    // would not even look at the IL, but only use them to
+                    // resolve surface area.
+                    args.Append("-p skip ").AppendLine(Quote(assemblyName));
+                }
+            }
+
+            if (OutputDirectory != null)
+            {
+                args.Append("-out ").AppendLine(Quote(OutputDirectory.ItemSpec));
+            }
+
+            if (ClearInitLocals)
+            {
+                args.AppendLine("--enable-opt clearinitlocals");
+                if ((ClearInitLocalsAssemblies != null) && (ClearInitLocalsAssemblies.Length > 0))
+                {
+                    args.Append("-m ClearInitLocalsAssemblies ");
+                    args.AppendLine(ClearInitLocalsAssemblies);
+                }
+            }
+
+            if (ExtraArgs != null)
+            {
+                args.AppendLine(ExtraArgs);
+            }
+
+            if (DumpDependencies)
+            {
+                args.AppendLine("--dump-dependencies");
+            }
+
+            return args.ToString();
+        }
+
+        protected override bool HandleTaskExecutionErrors()
+        {
+            // Show a slightly better error than the standard ToolTask message that says "dotnet" failed.
+            Log.LogError($"ILLink failed with exited code {ExitCode}.");
+            return false;
+        }
+
+        protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance)
+        {
+            if (!string.IsNullOrEmpty(singleLine) && singleLine.StartsWith("Unhandled exception.", StringComparison.Ordinal))
+            {
+                // The Mono linker currently prints out an entire stack trace when the linker fails.
+                // We want to show something actionable in the VS Error window.
+                Log.LogError(singleLine);
+            }
+            else
+            {
+                base.LogEventsFromTextOutput(singleLine, messageImportance);
+            }
+        }
+    }
+}

+ 74 - 0
src/Components/Blazor/Build/src/Tasks/GenerateBlazorBootJson.cs

@@ -0,0 +1,74 @@
+// 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.Linq;
+using System.Reflection;
+using System.Runtime.Serialization.Json;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    public class GenerateBlazorBootJson : Task
+    {
+        [Required]
+        public string AssemblyPath { get; set; }
+
+        [Required]
+        public ITaskItem[] References { get; set; }
+
+        [Required]
+        public bool LinkerEnabled { get; set; }
+
+        [Required]
+        public string OutputPath { get; set; }
+
+        public override bool Execute()
+        {
+            var entryAssemblyName = AssemblyName.GetAssemblyName(AssemblyPath).Name;
+            var assemblies = References.Select(c => Path.GetFileName(c.ItemSpec)).ToArray();
+
+            using var fileStream = File.Create(OutputPath);
+            WriteBootJson(fileStream, entryAssemblyName, assemblies, LinkerEnabled);
+
+            return true;
+        }
+
+        internal static void WriteBootJson(Stream stream, string entryAssemblyName, string[] assemblies, bool linkerEnabled)
+        {
+            var data = new BootJsonData
+            {
+                entryAssembly = entryAssemblyName,
+                assemblies = assemblies,
+                linkerEnabled = linkerEnabled,
+            };
+
+            var serializer = new DataContractJsonSerializer(typeof(BootJsonData));
+            serializer.WriteObject(stream, data);
+        }
+
+        /// <summary>
+        /// Defines the structure of a Blazor boot JSON file
+        /// </summary>
+#pragma warning disable IDE1006 // Naming Styles
+        public class BootJsonData
+        {
+            /// <summary>
+            /// Gets the name of the assembly with the application entry point
+            /// </summary>
+            public string entryAssembly { get; set; }
+
+            /// <summary>
+            /// Gets the closure of assemblies to be loaded by Blazor WASM. This includes the application entry assembly.
+            /// </summary>
+            public string[] assemblies { get; set; }
+
+            /// <summary>
+            /// Gets a value that determines if the linker is enabled.
+            /// </summary>
+            public bool linkerEnabled { get; set; }
+        }
+#pragma warning restore IDE1006 // Naming Styles
+    }
+}

+ 203 - 0
src/Components/Blazor/Build/src/Tasks/ResolveBlazorRuntimeDependencies.cs

@@ -0,0 +1,203 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    public class ResolveBlazorRuntimeDependencies : Task
+    {
+        [Required]
+        public string EntryPoint { get; set; }
+
+        [Required]
+        public ITaskItem[] ApplicationDependencies { get; set; }
+
+        [Required]
+        public ITaskItem[] WebAssemblyBCLAssemblies { get; set; }
+
+        [Output]
+        public ITaskItem[] Dependencies { get; set; }
+
+        public override bool Execute()
+        {
+            var paths = ResolveRuntimeDependenciesCore(EntryPoint, ApplicationDependencies.Select(c => c.ItemSpec), WebAssemblyBCLAssemblies.Select(c => c.ItemSpec));
+            Dependencies = paths.Select(p => new TaskItem(p)).ToArray();
+
+            return true;
+        }
+
+        public static IEnumerable<string> ResolveRuntimeDependenciesCore(
+            string entryPoint,
+            IEnumerable<string> applicationDependencies,
+            IEnumerable<string> monoBclAssemblies)
+        {
+            var entryAssembly = new AssemblyEntry(entryPoint, GetAssemblyName(entryPoint));
+
+            var dependencies = CreateAssemblyLookup(applicationDependencies);
+
+            var bcl = CreateAssemblyLookup(monoBclAssemblies);
+
+            var assemblyResolutionContext = new AssemblyResolutionContext(
+                entryAssembly,
+                dependencies,
+                bcl);
+
+            assemblyResolutionContext.ResolveAssemblies();
+
+            var paths = assemblyResolutionContext.Results.Select(r => r.Path);
+            return paths.Concat(FindPdbs(paths));
+
+            static Dictionary<string, AssemblyEntry> CreateAssemblyLookup(IEnumerable<string> assemblyPaths)
+            {
+                var dictionary = new Dictionary<string, AssemblyEntry>(StringComparer.Ordinal);
+                foreach (var path in assemblyPaths)
+                {
+                    var assemblyName = AssemblyName.GetAssemblyName(path).Name;
+                    if (dictionary.TryGetValue(assemblyName, out var previous))
+                    {
+                        throw new InvalidOperationException($"Multiple assemblies found with the same assembly name '{assemblyName}':" +
+                            Environment.NewLine + string.Join(Environment.NewLine, previous, path));
+                    }
+                    dictionary[assemblyName] = new AssemblyEntry(path, assemblyName);
+                }
+
+                return dictionary;
+            }
+        }
+
+        private static string GetAssemblyName(string assemblyPath)
+        {
+            return AssemblyName.GetAssemblyName(assemblyPath).Name;
+        }
+
+        private static IEnumerable<string> FindPdbs(IEnumerable<string> dllPaths)
+        {
+            return dllPaths
+                .Select(path => Path.ChangeExtension(path, "pdb"))
+                .Where(path => File.Exists(path));
+        }
+
+        public class AssemblyResolutionContext
+        {
+            public AssemblyResolutionContext(
+                AssemblyEntry entryAssembly,
+                Dictionary<string, AssemblyEntry> dependencies,
+                Dictionary<string, AssemblyEntry> bcl)
+            {
+                EntryAssembly = entryAssembly;
+                Dependencies = dependencies;
+                Bcl = bcl;
+            }
+
+            public AssemblyEntry EntryAssembly { get; }
+            public Dictionary<string, AssemblyEntry> Dependencies { get; }
+            public Dictionary<string, AssemblyEntry> Bcl { get; }
+
+            public IList<AssemblyEntry> Results { get; } = new List<AssemblyEntry>();
+
+            internal void ResolveAssemblies()
+            {
+                var visitedAssemblies = new HashSet<string>();
+                var pendingAssemblies = new Stack<string>();
+                pendingAssemblies.Push(EntryAssembly.Name);
+                ResolveAssembliesCore();
+
+                void ResolveAssembliesCore()
+                {
+                    while (pendingAssemblies.Count > 0)
+                    {
+                        var current = pendingAssemblies.Pop();
+                        if (visitedAssemblies.Add(current))
+                        {
+                            // Not all references will be resolvable within the Mono BCL.
+                            // Skipping unresolved assemblies here is equivalent to passing "--skip-unresolved true" to the Mono linker.
+                            if (Resolve(current) is AssemblyEntry resolved)
+                            {
+                                Results.Add(resolved);
+                                var references = GetAssemblyReferences(resolved.Path);
+                                foreach (var reference in references)
+                                {
+                                    pendingAssemblies.Push(reference);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                AssemblyEntry? Resolve(string assemblyName)
+                {
+                    if (EntryAssembly.Name == assemblyName)
+                    {
+                        return EntryAssembly;
+                    }
+
+                    // Resolution logic. For right now, we will prefer the mono BCL version of a given
+                    // assembly if there is a candidate assembly and an equivalent mono assembly.
+                    if (Bcl.TryGetValue(assemblyName, out var assembly) ||
+                        Dependencies.TryGetValue(assemblyName, out assembly))
+                    {
+                        return assembly;
+                    }
+
+                    return null;
+                }
+
+                static IReadOnlyList<string> GetAssemblyReferences(string assemblyPath)
+                {
+                    try
+                    {
+                        using var peReader = new PEReader(File.OpenRead(assemblyPath));
+                        if (!peReader.HasMetadata)
+                        {
+                            return Array.Empty<string>(); // not a managed assembly
+                        }
+
+                        var metadataReader = peReader.GetMetadataReader();
+
+                        var references = new List<string>();
+                        foreach (var handle in metadataReader.AssemblyReferences)
+                        {
+                            var reference = metadataReader.GetAssemblyReference(handle);
+                            var referenceName = metadataReader.GetString(reference.Name);
+
+                            references.Add(referenceName);
+                        }
+
+                        return references;
+                    }
+                    catch (BadImageFormatException)
+                    {
+                        // not a PE file, or invalid metadata
+                    }
+
+                    return Array.Empty<string>(); // not a managed assembly
+                }
+            }
+        }
+
+        [DebuggerDisplay("{ToString(),nq}")]
+        public readonly struct AssemblyEntry
+        {
+            public AssemblyEntry(string path, string name)
+            {
+                Path = path;
+                Name = name;
+            }
+
+            public string Path { get; }
+            public string Name { get; }
+
+            public override string ToString() => Name;
+        }
+    }
+}

+ 17 - 17
src/Components/Blazor/Build/src/targets/All.targets

@@ -6,8 +6,10 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <BlazorToolsDir Condition="'$(BlazorToolsDir)' == ''">$(MSBuildThisFileDirectory)../tools/</BlazorToolsDir>
-    <BlazorBuildExe>dotnet &quot;$(BlazorToolsDir)Microsoft.AspNetCore.Blazor.Build.dll&quot;</BlazorBuildExe>
+    <BlazorToolsDir Condition="'$(BlazorToolsDir)' == ''">$(MSBuildThisFileDirectory)..\tools\</BlazorToolsDir>
+    <_BlazorTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp</_BlazorTasksTFM>
+    <_BlazorTasksTFM Condition=" '$(_BlazorTasksTFM)' == ''">netfx</_BlazorTasksTFM>
+    <BlazorTasksPath>$(BlazorToolsDir)$(_BlazorTasksTFM)\Microsoft.AspNetCore.Blazor.Build.Tasks.dll</BlazorTasksPath>
 
     <!-- The Blazor build code can only find your referenced assemblies if they are in the output directory -->
     <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@@ -15,31 +17,29 @@
 
   <Import Project="Blazor.MonoRuntime.targets" />
   <Import Project="Publish.targets" />
+  <Import Project="StaticWebAssets.targets" />
 
-  <Target Name="GenerateBlazorMetadataFile" BeforeTargets="GetCopyToOutputDirectoryItems">
+  <Target Name="GenerateBlazorMetadataFile"
+    BeforeTargets="GetCopyToOutputDirectoryItems">
     <PropertyGroup>
       <BlazorMetadataFileName>$(AssemblyName).blazor.config</BlazorMetadataFileName>
       <BlazorMetadataFilePath>$(TargetDir)$(BlazorMetadataFileName)</BlazorMetadataFilePath>
     </PropertyGroup>
-    <WriteLinesToFile File="$(BlazorMetadataFilePath)" Lines="$(MSBuildProjectFullPath)" Overwrite="true" Encoding="Unicode"/>
-    <WriteLinesToFile File="$(BlazorMetadataFilePath)" Lines="$(OutDir)$(AssemblyName).dll" Overwrite="false" Encoding="Unicode"/>
-    <WriteLinesToFile File="$(BlazorMetadataFilePath)" Condition="'$(BlazorRebuildOnFileChange)'=='true'" Lines="autorebuild:true" Overwrite="false" Encoding="Unicode"/>
-    <WriteLinesToFile File="$(BlazorMetadataFilePath)" Condition="'$(BlazorEnableDebugging)'=='true'" Lines="debug:true" Overwrite="false" Encoding="Unicode"/>
+
     <ItemGroup>
-      <ContentWithTargetPath Include="$(BlazorMetadataFilePath)" TargetPath="$(BlazorMetadataFileName)" CopyToOutputDirectory="PreserveNewest" />
+      <_BlazorConfigContent Include="$(MSBuildProjectFullPath)" />
+      <_BlazorConfigContent Include="$(TargetPath)" />
+      <_BlazorConfigContent Include="debug:true" Condition="'$(BlazorEnableDebugging)'=='true'" />
     </ItemGroup>
-  </Target>
 
-  <PropertyGroup>
-    <GetCurrentProjectStaticWebAssetsDependsOn>
-      $(GetCurrentProjectStaticWebAssetsDependsOn);
-      _ClearCurrentStaticWebAssetsForReferenceDiscovery
-    </GetCurrentProjectStaticWebAssetsDependsOn>
-  </PropertyGroup>
+    <WriteLinesToFile
+      File="$(BlazorMetadataFilePath)"
+      Lines="@(_BlazorConfigContent)"
+      Overwrite="true"
+      WriteOnlyWhenDifferent="True" />
 
-  <Target Name="_ClearCurrentStaticWebAssetsForReferenceDiscovery">
     <ItemGroup>
-      <StaticWebAsset Remove="@(StaticWebAsset)" Condition="'%(SourceType)' == ''" />
+      <ContentWithTargetPath Include="$(BlazorMetadataFilePath)" TargetPath="$(BlazorMetadataFileName)" CopyToOutputDirectory="PreserveNewest" />
     </ItemGroup>
   </Target>
 

+ 9 - 11
src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.props

@@ -1,22 +1,20 @@
 <Project>
 
-  <PropertyGroup Condition="'$(BlazorBuildReferenceFromSource)'==''">
-    <BlazorJsPath>$(MSBuildThisFileDirectory)../tools/blazor/blazor.*.js</BlazorJsPath>
+  <PropertyGroup>
+    <BlazorJsPath Condition="'$(BlazorJsPath)' == ''">$(MSBuildThisFileDirectory)..\tools\blazor\blazor.webassembly.js</BlazorJsPath>
   </PropertyGroup>
 
   <PropertyGroup Label="Blazor build outputs">
     <MonoLinkerI18NAssemblies>none</MonoLinkerI18NAssemblies> <!-- See Mono linker docs - allows comma-separated values from: none,all,cjk,mideast,other,rare,west -->
     <AdditionalMonoLinkerOptions>--disable-opt unreachablebodies --verbose --strip-security true --exclude-feature com --exclude-feature sre -v false -c link -u link -b true</AdditionalMonoLinkerOptions>
-    <BaseBlazorDistPath>dist/</BaseBlazorDistPath>
-    <BaseBlazorPackageContentOutputPath>$(BaseBlazorDistPath)_content/</BaseBlazorPackageContentOutputPath>
-    <BaseBlazorRuntimeOutputPath>$(BaseBlazorDistPath)_framework/</BaseBlazorRuntimeOutputPath>
-    <BaseBlazorRuntimeBinOutputPath>$(BaseBlazorRuntimeOutputPath)_bin/</BaseBlazorRuntimeBinOutputPath>
-    <BaseBlazorRuntimeWasmOutputPath>$(BaseBlazorRuntimeOutputPath)wasm/</BaseBlazorRuntimeWasmOutputPath>
-    <BaseBlazorJsOutputPath>$(BaseBlazorRuntimeOutputPath)</BaseBlazorJsOutputPath>
-    <BaseBlazorIntermediateOutputPath>blazor/</BaseBlazorIntermediateOutputPath>
-    <BlazorWebRootName>wwwroot/</BlazorWebRootName>
+    <BaseBlazorDistPath>dist\</BaseBlazorDistPath>
+    <BaseBlazorPackageContentOutputPath>$(BaseBlazorDistPath)_content\</BaseBlazorPackageContentOutputPath>
+    <BaseBlazorRuntimeOutputPath>$(BaseBlazorDistPath)_framework\</BaseBlazorRuntimeOutputPath>
+    <BlazorRuntimeBinOutputPath>$(BaseBlazorRuntimeOutputPath)_bin\</BlazorRuntimeBinOutputPath>
+    <BlazorRuntimeWasmOutputPath>$(BaseBlazorRuntimeOutputPath)wasm\</BlazorRuntimeWasmOutputPath>
+    <BlazorWebRootName>wwwroot\</BlazorWebRootName>
     <BlazorBootJsonName>blazor.boot.json</BlazorBootJsonName>
-    <BlazorBootJsonOutputPath>$(BaseBlazorRuntimeOutputPath)$(BlazorBootJsonName)</BlazorBootJsonOutputPath>
+    <_BlazorBuiltInBclLinkerDescriptor>$(MSBuildThisFileDirectory)BuiltInBclLinkerDescriptor.xml</_BlazorBuiltInBclLinkerDescriptor>
   </PropertyGroup>
 
 </Project>

+ 159 - 530
src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets

@@ -1,206 +1,85 @@
 <Project>
+  <PropertyGroup>
+    <BlazorLinkOnBuild Condition="$(BlazorLinkOnBuild) == ''">true</BlazorLinkOnBuild>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <!-- Stop-gap until we can migrate Blazor.Mono package to use better naming convention -->
+    <DotNetWebAssemblyBCLPath Condition="'$(DotNetWebAssemblyBCLPath)' == '' AND '$(MonoBaseClassLibraryPath)' != ''">$(MonoBaseClassLibraryPath)</DotNetWebAssemblyBCLPath>
+    <DotNetWebAssemblyBCLFacadesPath Condition="'$(DotNetWebAssemblyBCLFacadesPath)' == '' AND '$(MonoBaseClassLibraryFacadesPath)' != ''">$(MonoBaseClassLibraryFacadesPath)</DotNetWebAssemblyBCLFacadesPath>
+    <DotNetWebAssemblyRuntimePath Condition="'$(DotNetWebAssemblyRuntimePath)' == '' AND '$(MonoWasmRuntimePath)' != ''">$(MonoWasmRuntimePath)</DotNetWebAssemblyRuntimePath>
+    <DotNetWebAssemblyFrameworkPath Condition="'$(DotNetWebAssemblyFrameworkPath)' == '' AND '$(MonoWasmFrameworkPath)' != ''">$(MonoWasmFrameworkPath)</DotNetWebAssemblyFrameworkPath>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(DotNetWebAssemblyArtifactsRoot)' != ''">
+    <!-- Compute paths given a path to DotNet WASM artifacts. This is meant to make it easy to test WASM builds -->
+    <DotNetWebAssemblyBCLPath>$(DotNetWebAssemblyArtifactsRoot)\wasm-bcl\wasm\</DotNetWebAssemblyBCLPath>
+    <DotNetWebAssemblyBCLFacadesPath>$(DotNetWebAssemblyBCLPath)\Facades\</DotNetWebAssemblyBCLFacadesPath>
+    <DotNetWebAssemblyRuntimePath>$(DotNetWebAssemblyArtifactsRoot)\builds\debug\</DotNetWebAssemblyRuntimePath>
+    <DotNetWebAssemblyFrameworkPath>$(DotNetWebAssemblyArtifactsRoot)\framework\</DotNetWebAssemblyFrameworkPath>
+  </PropertyGroup>
 
   <Target
     Name="_BlazorCopyFilesToOutputDirectory"
     DependsOnTargets="PrepareBlazorOutputs"
-    Inputs="@(BlazorItemOutput)"
-    Outputs="@(BlazorItemOutput->'%(TargetOutputPath)')"
+    Inputs="@(BlazorOutputWithTargetPath)"
+    Outputs="@(BlazorOutputWithTargetPath->'$(TargetDir)%(TargetOutputPath)')"
     AfterTargets="CopyFilesToOutputDirectory"
     Condition="'$(OutputType.ToLowerInvariant())'=='exe'">
 
     <!-- Copy the blazor output files  -->
     <Copy
-      SourceFiles="@(BlazorItemOutput)"
-      DestinationFiles="@(BlazorItemOutput->'%(TargetOutputPath)')"
+      SourceFiles="@(BlazorOutputWithTargetPath)"
+      DestinationFiles="@(BlazorOutputWithTargetPath->'$(TargetDir)%(TargetOutputPath)')"
       SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
       OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
       Retries="$(CopyRetryCount)"
       RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
       UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)"
       UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)"
-      Condition="'@(BlazorItemOutput)' != '' and '$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'">
+      Condition="'@(BlazorOutputWithTargetPath)' != '' and '$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'">
     </Copy>
 
     <ItemGroup>
-      <FileWrites Include="@(BlazorItemOutput->'%(TargetOutputPath)')" />
+      <FileWrites Include="@(BlazorOutputWithTargetPath->'$(TargetDir)%(TargetOutputPath)')" />
     </ItemGroup>
-  </Target>
-
-  <Target Name="_BlazorTrackResolveReferencesDidRun" AfterTargets="ResolveReferences">
-    <PropertyGroup>
-      <!-- So we know we can trust @(ReferenceCopyLocalPaths) later -->
-      <_BlazorResolveReferencesDidRun>true</_BlazorResolveReferencesDidRun>
-    </PropertyGroup>
-  </Target>
-
-  <Target Name="_BlazorBuildReport"
-          AfterTargets="_BlazorCopyFilesToOutputDirectory">
 
     <ItemGroup>
-      <_BlazorStatisticsOutput Include="@(BlazorItemOutput->'%(TargetOutputPath)')" />
+      <_BlazorStatisticsOutput Include="@(BlazorOutputWithTargetPath->'%(TargetOutputPath)')" />
     </ItemGroup>
-    <PropertyGroup>
-      <_BlazorStatisticsReportImportance Condition="'$(BlazorOutputStatistics)' == ''">normal</_BlazorStatisticsReportImportance>
-      <_BlazorStatisticsReportImportance Condition="'$(BlazorOutputStatistics)' != ''">high</_BlazorStatisticsReportImportance>
-    </PropertyGroup>
 
-    <Message Importance="high" Text="Blazor Build result -> @(_BlazorStatisticsOutput->Distinct()->Count()) files in $(TargetDir)dist" />
-    <Message Importance="$(_BlazorStatisticsReportImportance)" Text="%(_BlazorStatisticsOutput.Identity)" />
+    <Message Importance="high" Text="$(TargetName) (Blazor output) -> $(TargetDir)dist" />
   </Target>
 
-  <!-- Preparing blazor files for output:
-    PrepareBlazorOutputs
-      _PrepareBlazorOutputConfiguration
-      _DefineBlazorCommonInputs
-      _BlazorResolveOutputBinaries
-        When link on build:
-          _GenerateLinkerDescriptor
-          _CollectBlazorLinkerDescriptors
-          _LinkBlazorApplication
-          _CollectLinkerOutputs
-        When don't link on build:
-          _CollectResolvedAssemblies
-            _ResolveBlazorApplicationAssemblies
-            _ReadResolvedBlazorApplicationAssemblies
-            _IntermediateCopyBlazorApplicationAssemblies
-            _TouchBlazorApplicationAssemblies
-      _GenerateBlazorBootJson
-    _BlazorCopyFilesToOutputDirectory
-
-    The process for doing builds goes as follows:
-    Produce a hash file with the Hash SDK task and write that hash to a marker file.
-    Produce a marker file that saves whether we are linking or not in this build so that we can take that as
-    input in future builds and do the correct thing for incremental builds.
-    We only produce marker files when the input changes, if the input doesn't change the marker stays the
-    same.
-
-    If we are linking on this build the process is as follows:
-    1) We determine if there are linker descriptors available, if not generate one.
-    2) Collect the list of linker descriptors and create a marker for the linker if it doesn't exist or changed
-       from a previous build.
-    3) Run the linker in case the linker inputs marker is newer than the linker result file.
-    4) Collect the outputs from the linker.
-
-    If we are not linking in this build the process is as follows:
-    1) Resolve the assemblies for the application only if the inputs marker is newer than the resolved assemblies
-       result file.
-    2) Read the result file with the resolved assemblies.
-    3) Copy the resolved assemblies to an intermediate folder.
-    4) In case we are switching from linking to not linking, touch the files in the intermediate folder to ensure
-       that updated versions of the files get copied to the output folder.
-
-    Once the binary outputs are resolved:
-    1) Create a marker file with the resolved assemblies and the boot json data as inputs.
-    2) If the marker file is newer than the boot json in the output folder, regenerate the
-       boot json
-
-    Once all the outputs are resolved (static content + binary outputs + boot json)
-    Copy all the files to the output folder.
-    -->
-
-  <PropertyGroup>
-    <PrepareBlazorOutputs>
-      _PrepareBlazorOutputConfiguration;
-      _DefineBlazorCommonInputs;
-      _BlazorResolveOutputBinaries;
-      _GenerateBlazorBootJson;
-    </PrepareBlazorOutputs>
-  </PropertyGroup>
-
-  <Target Name="PrepareBlazorOutputs" DependsOnTargets="$(PrepareBlazorOutputs)" />
-
-  <!--
-  Prepare blazor outputs preamble:
-  * Creates updated marker files (if necessary) for incremental builds.
-  * Computes intermediate and final output paths.
-  * Computes the list of static items to copy to the output folder.
-  -->
-
-  <Target Name="_PrepareBlazorOutputConfiguration">
-    <!--
-    This task produces all the "final" paths for all the files we need to produce the final output.
-
-    The final folder is something like bin/<<Configuration>>/<<TargetFramework>>/dist
-    /_framework/_bin <- This will contain either the BCL + app assemblies or the result of linking the app.
-    /_framework/wasm <- This will contain the wsm runtime copied from the nuget package.
-    /_framework/blazor.js <- This is the blazor.js file copied from the nuget package.
-    /_framework/blazor.boot.json <- This is the boot json file
-
-    This task also defines some intermediate paths that we will use:
-    /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker <- This will be used to create the output from the linker.
-    /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linked.assemblies.txt <- This will be used to save the output files from
-    the linker and use that as marker to identify whether or not we need to run the linker.
-    /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker.descriptor.xml <- This will be used to generate an XML descriptor
-    for the mono linker.
-    /obj/<<configuration>>/<<targetframework>>/blazor/inputs.basic.cache <- This is the marker file to track the inputs common
-    inputs to the output generation process.
-    /obj/<<configuration>>/<<targetframework>>/blazor/inputs.copylocal.txt <- Paths to all the copy-local referenced assemblies found
-    during the build process (i.e., the @(ReferenceCopyLocalPaths) values). We need this because when publishing, the build doesn't
-    necessarily also run so this is the only way we know which assemblies to include in linking/resolveassemblies.
-    /obj/<<configuration>>/<<targetframework>>/blazor/inputs.linkerswitch.cache <- This is the marker file to track the
-    switch from linking to not linking and viceversa.
-    /obj/<<configuration>>/<<targetframework>>/blazor/inputs.linker.cache <- This is the marker file to track the inputs
-    to the linker.
-    /obj/<<configuration>>/<<targetframework>>/blazor/resolvedassemblies/ <- This will be used to store the resolved assemblies
-    before copying them to the output when linking is not enabled.
-    /obj/<<configuration>>/<<targetframework>>/blazor/resolved.assemblies.txt <- This keeps track of all the resolved assemblies.
-    /obj/<<configuration>>/<<targetframework>>/blazor/blazor.boot.json <- The generated boot json file
-    /obj/<<configuration>>/<<targetframework>>/blazor/inputs.bootjson.cache <- The marker file that track whether boot json needs to
-    be regenerated.
-    -->
-
-    <PropertyGroup Label="Build properties">
-      <_BlazorShouldLinkApplicationAssemblies Condition="$(BlazorLinkOnBuild) == 'false'"></_BlazorShouldLinkApplicationAssemblies>
-      <_BlazorShouldLinkApplicationAssemblies Condition="$(BlazorLinkOnBuild) == 'true'">true</_BlazorShouldLinkApplicationAssemblies>
-      <_BlazorBuiltInBclLinkerDescriptor>$(MSBuildThisFileDirectory)BuiltInBclLinkerDescriptor.xml</_BlazorBuiltInBclLinkerDescriptor>
-    </PropertyGroup>
+  <Target
+    Name="PrepareBlazorOutputs"
+    DependsOnTargets="_ResolveBlazorInputs;_ResolveBlazorOutputs;_GenerateBlazorBootJson">
 
-    <ItemGroup Label="Static content to copy to the output folder">
-      <MonoWasmFile Include="$(MonoWasmRuntimePath)**/*.*" />
-      <BlazorJsFile Include="$(BlazorJsPath)" />
-      <BlazorItemOutput Include="@(MonoWasmFile)">
-        <TargetOutputPath>$(TargetDir)$(BaseBlazorRuntimeWasmOutputPath)%(FileName)%(Extension)</TargetOutputPath>
-        <Type>WebAssembly</Type>
-        <IsStatic>true</IsStatic>
-      </BlazorItemOutput>
-      <BlazorItemOutput Include="@(BlazorJsFile)">
-        <TargetOutputPath>$(TargetDir)$(BaseBlazorJsOutputPath)%(FileName)%(Extension)</TargetOutputPath>
-        <Type>BlazorRuntime</Type>
-        <IsStatic>true</IsStatic>
-      </BlazorItemOutput>
+    <ItemGroup>
+      <MonoWasmFile Include="$(DotNetWebAssemblyRuntimePath)*" />
+      <BlazorJSFile Include="$(BlazorJSPath)" />
+      <BlazorJSFile Include="$(BlazorJSMapPath)" Condition="Exists('$(BlazorJSMapPath)')" />
+
+      <BlazorOutputWithTargetPath Include="@(MonoWasmFile)">
+        <TargetOutputPath>$(BlazorRuntimeWasmOutputPath)%(FileName)%(Extension)</TargetOutputPath>
+      </BlazorOutputWithTargetPath>
+      <BlazorOutputWithTargetPath Include="@(BlazorJSFile)">
+        <TargetOutputPath>$(BaseBlazorRuntimeOutputPath)%(FileName)%(Extension)</TargetOutputPath>
+      </BlazorOutputWithTargetPath>
     </ItemGroup>
 
-    <Error Condition="'@(BlazorJsFile->Count())' == '0'" Text="No JS files found in '$(BlazorJsPath)'" />
-
     <ItemGroup Label="Static content supplied by NuGet packages">
       <_BlazorPackageContentOutput Include="@(BlazorPackageContentFile)" Condition="%(SourcePackage) != ''">
-        <TargetOutputPath>$(TargetDir)$(BaseBlazorPackageContentOutputPath)%(SourcePackage)\%(RecursiveDir)\%(Filename)%(Extension)</TargetOutputPath>
-        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        <TargetOutputPath>$(BaseBlazorPackageContentOutputPath)%(SourcePackage)\%(RecursiveDir)\%(Filename)%(Extension)</TargetOutputPath>
       </_BlazorPackageContentOutput>
-      <BlazorItemOutput Include="@(_BlazorPackageContentOutput)" />
+      <BlazorOutputWithTargetPath Include="@(_BlazorPackageContentOutput)" />
     </ItemGroup>
+  </Target>
 
-    <PropertyGroup Label="Intermediate output paths">
-
+  <Target Name="_ResolveBlazorInputs">
+    <PropertyGroup>
       <!-- /obj/<<configuration>>/<<targetframework>>/blazor -->
-      <BlazorIntermediateOutputPath>$(IntermediateOutputPath)$(BaseBlazorIntermediateOutputPath)</BlazorIntermediateOutputPath>
-      <BlazorIntermediateOutputPath Condition="! $([System.IO.Path]::IsPathRooted($(BlazorIntermediateOutputPath)))">$([MSBuild]::Escape($([System.IO.Path]::GetFullPath('$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(BlazorIntermediateOutputPath)'))'))))</BlazorIntermediateOutputPath>
-
-      <!-- Common marker files paths -->
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.basic.cache -->
-      <BlazorBuildCommonInputsCache>$(BlazorIntermediateOutputPath)inputs.basic.cache</BlazorBuildCommonInputsCache>
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.copylocal.txt -->
-      <BlazorLocalReferencesOutputPath>$(BlazorIntermediateOutputPath)inputs.copylocal.txt</BlazorLocalReferencesOutputPath>
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.linkerswitch.cache -->
-      <BlazorBuildLinkerSwitchInputsCache>$(BlazorIntermediateOutputPath)inputs.linkerswitch.cache</BlazorBuildLinkerSwitchInputsCache>
-
-      <!-- Linker paths and marker files -->
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.linker.cache -->
-      <BlazorBuildLinkerInputsCache>$(BlazorIntermediateOutputPath)inputs.linker.cache</BlazorBuildLinkerInputsCache>
+      <BlazorIntermediateOutputPath>$(IntermediateOutputPath)blazor\</BlazorIntermediateOutputPath>
 
       <!-- /obj/<<configuration>>/<<targetframework>>/blazor/linker.descriptor.xml -->
       <GeneratedBlazorLinkerDescriptor>$(BlazorIntermediateOutputPath)linker.descriptor.xml</GeneratedBlazorLinkerDescriptor>
@@ -208,452 +87,202 @@
       <!-- /obj/<<configuration>>/<<targetframework>>/blazor/linker/ -->
       <BlazorIntermediateLinkerOutputPath>$(BlazorIntermediateOutputPath)linker/</BlazorIntermediateLinkerOutputPath>
 
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/linked.assemblies.txt -->
-      <BlazorIntermediateLinkerResultFilePath>$(BlazorIntermediateOutputPath)linked.assemblies.txt</BlazorIntermediateLinkerResultFilePath>
-
-      <!-- Resolved assemblies paths and marker files -->
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/resolvedassemblies/ -->
-      <BlazorIntermediateResolvedApplicationAssembliesOutputPath>$(BlazorIntermediateOutputPath)resolvedassemblies/</BlazorIntermediateResolvedApplicationAssembliesOutputPath>
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/resolved.assemblies.txt -->
-      <BlazorResolvedAssembliesOutputPath>$(BlazorIntermediateOutputPath)resolved.assemblies.txt</BlazorResolvedAssembliesOutputPath>
-
-      <!-- boot json related paths and markers -->
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/ -->
-      <BlazorBootJsonIntermediateOutputDir>$(BlazorIntermediateOutputPath)</BlazorBootJsonIntermediateOutputDir>
-
       <!-- /obj/<<configuration>>/<<targetframework>>/blazor/blazor.boot.json -->
-      <BlazorBootJsonIntermediateOutputPath>$(BlazorBootJsonIntermediateOutputDir)$(BlazorBootJsonName)</BlazorBootJsonIntermediateOutputPath>
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.bootjson.cache -->
-      <BlazorBuildBootJsonInputsCache>$(BlazorIntermediateOutputPath)inputs.bootjson.cache</BlazorBuildBootJsonInputsCache>
-
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/resolve-dependencies.txt -->
-      <BlazorResolveDependenciesFilePath>$(BlazorIntermediateOutputPath)resolve-dependencies.txt</BlazorResolveDependenciesFilePath>
+      <BlazorBootJsonIntermediateOutputPath>$(BlazorIntermediateOutputPath)$(BlazorBootJsonName)</BlazorBootJsonIntermediateOutputPath>
 
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/bootjson-references.txt -->
-      <BlazorBootJsonReferencesFilePath>$(BlazorIntermediateOutputPath)bootjson-references.txt</BlazorBootJsonReferencesFilePath>
+      <_BlazorLinkerOutputCache>$(BlazorIntermediateOutputPath)linker.output</_BlazorLinkerOutputCache>
 
-      <!-- /obj/<<configuration>>/<<targetframework>>/blazor/embedded.resources.txt -->
-      <BlazorEmbeddedResourcesConfigFilePath>$(BlazorIntermediateOutputPath)embedded.resources.txt</BlazorEmbeddedResourcesConfigFilePath>
-
-    </PropertyGroup>
-
-    <PropertyGroup Label="Final output paths">
-      <BlazorRuntimeBinOutputPath>$(TargetDir)$(BaseBlazorRuntimeBinOutputPath)</BlazorRuntimeBinOutputPath>
+      <_BlazorApplicationAssembliesCacheFile>$(BlazorIntermediateOutputPath)unlinked.output</_BlazorApplicationAssembliesCacheFile>
     </PropertyGroup>
 
-    <MakeDir Directories="$(BlazorIntermediateOutputPath)" />
-
-  </Target>
-
-  <Target Name="_DefineBlazorCommonInputs">
-    <!-- If ResolveReferences hasn't yet run, we must be inside a VS publish process
-         that doesn't also do a build, so use the stored information. -->
-    <ReadLinesFromFile
-      Condition="'$(_BlazorResolveReferencesDidRun)'!='true'"
-      File="$(BlazorLocalReferencesOutputPath)">
-      <Output TaskParameter="Lines" ItemName="_BlazorDependencyInput"/>
-    </ReadLinesFromFile>
-    <ItemGroup Condition="'$(_BlazorResolveReferencesDidRun)'=='true'">
-      <!-- ... otherwise we can get the fresh info from @(ReferenceCopyLocalPaths) -->
-      <_BlazorDependencyInput Include="@(ReferenceCopyLocalPaths->WithMetadataValue('Extension','.dll')->'%(FullPath)')" />
-    </ItemGroup>
-
     <ItemGroup>
-      <_BlazorCommonInput Include="@(IntermediateAssembly)" />
-      <_BlazorCommonInput Include="@(_BlazorDependencyInput)" />
-      <_BlazorCommonInput Include="$(_BlazorShouldLinkApplicationAssemblies)" />
-      <_BlazorCommonInput Include="$(BlazorEnableDebugging)" />
-      <_BlazorLinkingOption Condition="'$(_BlazorShouldLinkApplicationAssemblies)' == ''" Include="false" />
-      <_BlazorLinkingOption Condition="'$(_BlazorShouldLinkApplicationAssemblies)' != ''" Include="true" />
-    </ItemGroup>
+      <_BlazorDependencyInput Include="@(ReferenceCopyLocalPaths->WithMetadataValue('Extension','.dll')->'%(FullPath)')" />
 
-    <Hash ItemsToHash="@(_BlazorCommonInput)">
-      <Output TaskParameter="HashResult" PropertyName="_BlazorBuildBasicInputHash" />
-    </Hash>
-
-    <WriteLinesToFile
-      Lines="$(_BlazorBuildBasicInputHash)"
-      File="$(BlazorBuildCommonInputsCache)"
-      Overwrite="True"
-      WriteOnlyWhenDifferent="True" />
-
-    <WriteLinesToFile
-      Lines="@(_BlazorDependencyInput)"
-      File="$(BlazorLocalReferencesOutputPath)"
-      Overwrite="True"
-      WriteOnlyWhenDifferent="True" />
-
-    <!-- Switch to detect when we switch from linking to not linking and viceversa -->
-    <WriteLinesToFile
-      Lines="@(_BlazorLinkingOption)"
-      File="$(BlazorBuildLinkerSwitchInputsCache)"
-      Overwrite="True"
-      WriteOnlyWhenDifferent="True" />
+      <_WebAssemblyBCLFolder Include="
+        $(DotNetWebAssemblyBCLPath);
+        $(DotNetWebAssemblyBCLFacadesPath);
+        $(DotNetWebAssemblyFrameworkPath)" />
 
-    <ItemGroup>
-      <FileWrites Include="$(BlazorBuildLinkerSwitchInputsCache)" />
-      <FileWrites Include="$(BlazorBuildCommonInputsCache)" />
-      <FileWrites Include="$(BlazorLocalReferencesOutputPath)" />
+      <_WebAssemblyBCLAssembly Include="%(_WebAssemblyBCLFolder.Identity)*.dll" />
     </ItemGroup>
 
+    <MakeDir Directories="$(BlazorIntermediateOutputPath)" />
   </Target>
 
-  <Target Name="_BlazorResolveOutputBinaries" DependsOnTargets="_CollectLinkerOutputs;_CollectResolvedAssemblies" />
+  <Target Name="_ResolveBlazorOutputs" DependsOnTargets="_ResolveBlazorOutputsWhenLinked;_ResolveBlazorOutputsWhenNotLinked">
+    <Error
+      Message="Unrecongnized value for BlazorLinkOnBuild: '$(BlazorLinkOnBuild)'. Valid values are 'true' or 'false'."
+      Condition="'$(BlazorLinkOnBuild)' != 'true' AND '$(BlazorLinkOnBuild)' != 'false'" />
+  </Target>
 
   <!--
   Linker enabled part of the pipeline:
 
   * If there are no descriptors defined, generate a new linker descriptor.
-  * Collect the list of descriptors and produce a marker file to determine when the
-    inputs to the linker change in future builds.
-  * Invoke the linker if the linker inputs marker file is newer than the linker outputs.
-  * Read the outputs from the linker and add them to the list of blazor outputs.
+  * Invoke the linker and write linked files to a well-known directory.
+  * Collect the outputs of the linker.
   -->
 
-  <PropertyGroup>
-    <_CollectLinkerOutputsDependsOn>
-      _GenerateLinkerDescriptor;
-      _CollectBlazorLinkerDescriptors;
-      _LinkBlazorApplication
-    </_CollectLinkerOutputsDependsOn>
-  </PropertyGroup>
-
   <Target
-    Name="_CollectLinkerOutputs"
-    Condition="'$(_BlazorShouldLinkApplicationAssemblies)' != ''"
-    DependsOnTargets="$(_CollectLinkerOutputsDependsOn)">
-    <!--
-    Read the outputs from the linker (from this run or a previous run) and set them in an item group for
-    later use.
-    -->
-    <ReadLinesFromFile File="$(BlazorIntermediateLinkerResultFilePath)">
-      <Output TaskParameter="Lines" ItemName="_OptimizedFiles"/>
+    Name="_ResolveBlazorOutputsWhenLinked"
+    Condition="'$(BlazorLinkOnBuild)' == 'true'"
+    DependsOnTargets="_GenerateBlazorLinkerDescriptor;_LinkBlazorApplication">
+
+    <!-- _BlazorLinkerOutputCache records files linked during the last incremental build of the target. Read the contents and assign linked files to be copied to the output. -->
+    <ReadLinesFromFile File="$(_BlazorLinkerOutputCache)">
+      <Output TaskParameter="Lines" ItemName="_BlazorLinkedFile"/>
     </ReadLinesFromFile>
 
     <ItemGroup>
-      <BlazorItemOutput Include="@(_OptimizedFiles->WithMetadataValue('Extension','.dll'))">
-        <TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
-        <Type>Assembly</Type>
-        <PrimaryOutput Condition="'%(FileName)' == @(IntermediateAssembly->'%(FileName)')">true</PrimaryOutput>
-      </BlazorItemOutput>
-      <BlazorItemOutput Include="@(_OptimizedFiles->WithMetadataValue('Extension','.pdb'))">
+      <BlazorOutputWithTargetPath Include="%(_BlazorLinkedFile.Identity)">
         <TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
-        <Type>Pdb</Type>
-      </BlazorItemOutput>
-      <FileWrites Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->'%(TargetOutputPath)')" />
+      </BlazorOutputWithTargetPath>
     </ItemGroup>
-
   </Target>
 
-  <Target Name="_GenerateLinkerDescriptor"
-          Inputs="$(BlazorBuildCommonInputsCache)"
+  <UsingTask TaskName="BlazorCreateRootDescriptorFile" AssemblyFile="$(BlazorTasksPath)" />
+  <Target Name="_GenerateBlazorLinkerDescriptor"
+          Inputs="@(IntermediateAssembly)"
           Outputs="$(GeneratedBlazorLinkerDescriptor)"
-          Condition="$(_BlazorShouldLinkApplicationAssemblies) != '' and '@(BlazorLinkerDescriptor)' == ''">
+          Condition="'@(BlazorLinkerDescriptor)' == ''">
 
-    <ItemGroup>
-      <_PrepareLinkerDescriptorAssemblyLine Include="@(IntermediateAssembly->'%(FileName)')" />
-      <_GeneratedLinkerDescriptorLine Include="&lt;linker&gt;" />
-      <_GeneratedLinkerDescriptorLine Include="@(_PrepareLinkerDescriptorAssemblyLine->'&lt;assembly fullname=&quot;%(Identity)&quot; /&gt;')" />
-      <_GeneratedLinkerDescriptorLine Include="&lt;/linker&gt;" />
-    </ItemGroup>
+    <!-- Generate linker descriptors if the project doesn't explicitly provide one. -->
 
-    <WriteLinesToFile
-      Lines="@(_GeneratedLinkerDescriptorLine)"
-      File="$(GeneratedBlazorLinkerDescriptor)"
-      Overwrite="true"
-      WriteOnlyWhenDifferent="True" />
-
-  </Target>
-
-  <Target Name="_CollectBlazorLinkerDescriptors">
-
-    <ItemGroup Condition="@(BlazorLinkerDescriptor) == ''">
-      <BlazorLinkerDescriptor Include="$(_BlazorBuiltInBclLinkerDescriptor)" />
-      <BlazorLinkerDescriptor Include="$(GeneratedBlazorLinkerDescriptor)" />
-      <FileWrites Include="$(GeneratedBlazorLinkerDescriptor)" />
-    </ItemGroup>
+    <BlazorCreateRootDescriptorFile
+      AssemblyNames="@(IntermediateAssembly->'%(Filename)')"
+      RootDescriptorFilePath="$(GeneratedBlazorLinkerDescriptor)" />
 
     <ItemGroup>
-      <_BlazorLinkerInput Include="@(IntermediateAssembly)" />
-      <_BlazorLinkerInput Include="@(_BlazorDependencyInput)" />
-      <_BlazorLinkerInput Include="@(BlazorLinkerDescriptor)" />
-      <_BlazorLinkerInput Include="$(AdditionalMonoLinkerOptions)" />
-    </ItemGroup>
-
-    <Hash ItemsToHash="@(_BlazorLinkerInput)">
-      <Output TaskParameter="HashResult" PropertyName="_BlazorLinkerInputHash" />
-    </Hash>
-
-    <WriteLinesToFile
-      Lines="$(_BlazorLinkerInputHash)"
-      File="$(BlazorBuildLinkerInputsCache)"
-      Overwrite="True"
-      WriteOnlyWhenDifferent="True" />
-
-    <ItemGroup>
-      <FileWrites Include="$(BlazorBuildLinkerInputsCache)" />
+      <FileWrites Include="$(GeneratedBlazorLinkerDescriptor)" />
+      <BlazorLinkerDescriptor Include="$(GeneratedBlazorLinkerDescriptor)" />
+      <BlazorLinkerDescriptor Include="$(_BlazorBuiltInBclLinkerDescriptor)" />
     </ItemGroup>
-
   </Target>
 
+  <UsingTask TaskName="BlazorILLink" AssemblyFile="$(BlazorTasksPath)" />
+
   <Target
       Name="_LinkBlazorApplication"
-      Condition="$(_BlazorShouldLinkApplicationAssemblies) != ''"
-      Inputs="$(BlazorBuildLinkerInputsCache);
+      Inputs="$(ProjectAssetsFile);
               @(IntermediateAssembly);
               @(_BlazorDependencyInput);
-              @(BlazorLinkerDescriptor)"
-      Outputs="$(BlazorIntermediateLinkerResultFilePath)"
-    >
-    <!--
-    At this point we have decided to run the mono linker on the Blazor assembly and its dependencies.
-    The steps to run the mono linker are the following:
-    1) Clear the linker output directory if not clean before hand, as we don't know what the outputs of
-    the linker will be.
-    2) Run the linker on the main assembly, its dependencies and pass in the BCL folders to do the lookup
-    for framework assemblies.
-    3) Once we've run the linker we need to capture the produced output and generate a marker file containing
-    the list of produced files. This file will act as a marker to skip running the linker if none of the inputs
-    has changed.
-    4) Add the file we just created to the list of file writes, to support incremental builds.
-    -->
+              @(BlazorLinkerDescriptor);
+              $(MSBuildAllProjects)"
+      Outputs="$(_BlazorLinkerOutputCache)">
+
     <ItemGroup>
-      <_MonoBaseClassLibraryFolder Include="$(MonoBaseClassLibraryPath);$(MonoBaseClassLibraryFacadesPath);$(MonoWasmFrameworkPath)" />
-      <_BlazorAssembliesToLink Include="@(_BlazorDependencyInput->'-a &quot;%(Identity)&quot;')" />
-      <_BlazorAssembliesToLink Include="@(IntermediateAssembly->'-a &quot;%(FullPath)&quot;')" />
-      <_BlazorFolderLookupPaths Include="@(_MonoBaseClassLibraryFolder->'-d &quot;%(Identity)&quot;')" />
-      <_BlazorAssemblyDescriptorFiles
-        Include="@(BlazorLinkerDescriptor->'-x &quot;%(FullPath)&quot;')" Condition="'@(BlazorLinkerDescriptor)' != ''" />
+      <_BlazorDependencyAssembly Include="@(_BlazorDependencyInput)" IsLinkable="$([System.String]::Copy('%(FileName)').StartsWith('System.'))" />
+
+      <_BlazorAssemblyToLink Include="@(_WebAssemblyBCLAssembly)" />
+      <_BlazorAssemblyToLink Include="@(_BlazorDependencyAssembly)" Condition="'%(_BlazorDependencyAssembly.IsLinkable)' == 'true'" />
+
+      <_BlazorLinkerRoot Include="@(IntermediateAssembly)" />
+      <_BlazorLinkerRoot Include="@(_BlazorDependencyAssembly)" Condition="'%(_BlazorDependencyAssembly.IsLinkable)' != 'true'" />
     </ItemGroup>
 
     <PropertyGroup>
       <_BlazorLinkerAdditionalOptions>-l $(MonoLinkerI18NAssemblies) $(AdditionalMonoLinkerOptions)</_BlazorLinkerAdditionalOptions>
     </PropertyGroup>
 
-    <!-- Clear the contents of /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker -->
-    <Delete Files="$(BlazorIntermediateLinkerOutputPath)*.dll" />
-	
-	
-	<PropertyGroup>
-	  <_MonoLinkerDotNetPath>$(DOTNET_HOST_PATH)</_MonoLinkerDotNetPath>
-	  <_MonoLinkerDotNetPath Condition="'$(_MonoLinkerDotNetPath)' == ''">dotnet</_MonoLinkerDotNetPath>
-	</PropertyGroup>
-
-    <!-- Run the linker and put the results in /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker -->
-    <Exec Command="$(_MonoLinkerDotNetPath) &quot;$(MonoLinkerPath)&quot; $(_BlazorLinkerAdditionalOptions) @(_BlazorFolderLookupPaths, ' ') -o &quot;$(BlazorIntermediateLinkerOutputPath)&quot; @(_BlazorAssemblyDescriptorFiles, ' ') @(_BlazorAssembliesToLink, ' ')"  />
-
-    <!-- Collect the contents of /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker/ -->
     <ItemGroup>
-      <_BlazorLinkerOutput Include="$(BlazorIntermediateLinkerOutputPath)*.dll" />
-      <_BlazorLinkerOutput Include="$(BlazorIntermediateLinkerOutputPath)*.pdb" />
+      <_OldLinkedFile Include="$(BlazorIntermediateLinkerOutputPath)*.dll" />
+      <_OldLinkedFile Include="$(BlazorIntermediateLinkerOutputPath)*.pdb" />
     </ItemGroup>
 
+    <Delete Files="@(_OldLinkedFile)" />
+
     <!--
-    Write the list of files in /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker/ into
-    /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linked.assemblies.txt
+      When running from Desktop MSBuild, DOTNET_HOST_PATH is not set.
+      In this case, explicitly specify the path to the dotnet host.
     -->
-    <WriteLinesToFile
-      File="$(BlazorIntermediateLinkerResultFilePath)"
-      Lines="@(_BlazorLinkerOutput)"
-      Overwrite="true" />
+    <PropertyGroup Condition=" '$(DOTNET_HOST_PATH)' == '' ">
+      <_DotNetHostDirectory>$(NetCoreRoot)</_DotNetHostDirectory>
+      <_DotNetHostFileName>dotnet</_DotNetHostFileName>
+      <_DotNetHostFileName Condition=" '$(OS)' == 'Windows_NT' ">dotnet.exe</_DotNetHostFileName>
+    </PropertyGroup>
+
+    <BlazorILLink
+        ILLinkPath="$(MonoLinkerPath)"
+        AssemblyPaths="@(_BlazorAssemblyToLink)"
+        RootAssemblyNames="@(_BlazorLinkerRoot)"
+        RootDescriptorFiles="@(BlazorLinkerDescriptor)"
+        OutputDirectory="$(BlazorIntermediateLinkerOutputPath)"
+        ExtraArgs="$(_BlazorLinkerAdditionalOptions)"
+        ToolExe="$(_DotNetHostFileName)"
+        ToolPath="$(_DotNetHostDirectory)" />
 
-    <!-- Add /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linked.assemblies.txt to the list of written files. -->
-    <!-- Add /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker/*.dll to the list of written files. -->
     <ItemGroup>
-      <FileWrites Include="$(BlazorIntermediateLinkerResultFilePath)" />
-      <FileWrites Include="@(_BlazorLinkerOutput)" />
+      <_LinkerResult Include="$(BlazorIntermediateLinkerOutputPath)*.dll" />
+      <_LinkerResult Include="$(BlazorIntermediateLinkerOutputPath)*.pdb" Condition="'$(BlazorEnableDebugging)' == 'true'" />
     </ItemGroup>
-  </Target>
 
-  <!--
-  Linker disabled part of the pipeline:
-
-  * Run a CLI tool to produce the transitive closure of application references using the main application
-    as entry point.
-  * Read the list of resolved application references from the file produced by the previous step.
-  * Copy the resolved application references into an intermediate folder.
-  * If we are switching from linking to not linking
-    Touch the files in the intermediate folder to ensure they are copied to the output and replace
-    the linked versions with the same name.
-  * Collect the list of resolved assemblies in the intermediate output folder and prepare them to be
-    copied to their final destination in the output folder.
-  -->
+    <WriteLinesToFile File="$(_BlazorLinkerOutputCache)" Lines="@(_LinkerResult)" Overwrite="true" />
+  </Target>
 
-  <PropertyGroup>
-    <_CollectResolvedAssembliesDependsOn>
-      _ResolveBlazorApplicationAssemblies;
-      _ReadResolvedBlazorApplicationAssemblies;
-      _IntermediateCopyBlazorApplicationAssemblies;
-      _TouchBlazorApplicationAssemblies
-    </_CollectResolvedAssembliesDependsOn>
-  </PropertyGroup>
 
+  <UsingTask TaskName="ResolveBlazorRuntimeDependencies" AssemblyFile="$(BlazorTasksPath)" />
   <Target
-    Name="_CollectResolvedAssemblies"
-    DependsOnTargets="$(_CollectResolvedAssembliesDependsOn)"
-    Condition="'$(_BlazorShouldLinkApplicationAssemblies)' == ''">
+    Name="_ResolveBlazorOutputsWhenNotLinked"
+    DependsOnTargets="_ResolveBlazorRuntimeDependencies"
+    Condition="'$(BlazorLinkOnBuild)' != 'true'">
 
-    <!--
-    At this point we have decided not to run the linker and instead to just copy the assemblies
-    from the BCL referenced by the app the nuget package into the _framework/_bin folder.
-    The only thing we need to do here is collect the list of items that will go into _framework/_bin.
-    -->
+    <ReadLinesFromFile File="$(_BlazorApplicationAssembliesCacheFile)" Condition="'@(_BlazorResolvedRuntimeDependencies->Count())' == '0'">
+      <Output TaskParameter="Lines" ItemName="_BlazorResolvedRuntimeDependencies"/>
+    </ReadLinesFromFile>
 
     <ItemGroup>
-      <BlazorItemOutput Include="@(_IntermediateResolvedRuntimeDependencies->WithMetadataValue('Extension','.dll'))">
-        <TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
-        <Type>Assembly</Type>
-        <PrimaryOutput Condition="'%(FileName)' == @(IntermediateAssembly->'%(FileName)')">true</PrimaryOutput>
-      </BlazorItemOutput>
-      <BlazorItemOutput Include="@(_IntermediateResolvedRuntimeDependencies->WithMetadataValue('Extension','.pdb'))">
+      <BlazorOutputWithTargetPath Include="@(_BlazorResolvedRuntimeDependencies)">
         <TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
-        <Type>Pdb</Type>
-      </BlazorItemOutput>
-      <FileWrites Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->'%(TargetOutputPath)')" />
+      </BlazorOutputWithTargetPath>
     </ItemGroup>
   </Target>
 
   <Target
-    Name="_ResolveBlazorApplicationAssemblies"
-    Condition="'$(_BlazorShouldLinkApplicationAssemblies)' == ''"
-    Inputs="$(BlazorBuildCommonInputsCache);
+    Name="_ResolveBlazorRuntimeDependencies"
+    Inputs="$(ProjectAssetsFile);
             @(IntermediateAssembly);
             @(_BlazorDependencyInput)"
-    Outputs="$(BlazorResolvedAssembliesOutputPath)"
-  >
+    Outputs="$(_BlazorApplicationAssembliesCacheFile)">
 
-    <PropertyGroup>
-      <_ReferencesArg Condition="'@(_BlazorDependencyInput)' != ''">--references &quot;$(BlazorResolveDependenciesFilePath)&quot;</_ReferencesArg>
-      <_BclParameter>--base-class-library &quot;$(MonoBaseClassLibraryPath)&quot; --base-class-library &quot;$(MonoBaseClassLibraryFacadesPath)&quot; --base-class-library &quot;$(MonoWasmFrameworkPath)&quot;</_BclParameter>
-    </PropertyGroup>
-
-    <WriteLinesToFile
-          File="$(BlazorResolveDependenciesFilePath)"
-          Lines="@(_BlazorDependencyInput)"
-          Overwrite="true" />
-
-    <Exec Command="$(BlazorBuildExe) resolve-dependencies &quot;@(IntermediateAssembly->'%(FullPath)')&quot; $(_ReferencesArg) $(_BclParameter) --output &quot;$(BlazorResolvedAssembliesOutputPath)&quot;" />
-
-  </Target>
-
-  <Target Name="_ReadResolvedBlazorApplicationAssemblies">
-
-    <ReadLinesFromFile File="$(BlazorResolvedAssembliesOutputPath)">
-      <Output TaskParameter="Lines" ItemName="_BlazorResolvedRuntimeDependencies"/>
-    </ReadLinesFromFile>
+    <!--
+    At this point we have decided not to run the linker and instead to just copy the assemblies
+    from the BCL referenced by the app the nuget package into the _framework/_bin folder.
+    The only thing we need to do here is collect the list of items that will go into _framework/_bin.
+    -->
+    <ResolveBlazorRuntimeDependencies
+      EntryPoint="@(IntermediateAssembly)"
+      ApplicationDependencies="@(_BlazorDependencyInput)"
+      WebAssemblyBCLAssemblies="@(_WebAssemblyBCLAssembly)">
 
-    <ItemGroup>
-      <_IntermediateResolvedRuntimeDependencies Include="@(_BlazorResolvedRuntimeDependencies->'$(BlazorIntermediateResolvedApplicationAssembliesOutputPath)%(FileName)%(Extension)')" />
-    </ItemGroup>
+      <Output TaskParameter="Dependencies" ItemName="_BlazorResolvedRuntimeDependencies" />
+    </ResolveBlazorRuntimeDependencies>
 
     <ItemGroup>
-      <FileWrites Include="$(BlazorResolvedAssembliesOutputPath)" />
-      <FileWrites Include="@(_IntermediateResolvedRuntimeDependencies)" />
+      <FileWrites Include="$(_BlazorApplicationAssembliesCacheFile)" />
     </ItemGroup>
-
   </Target>
 
-  <Target
-    Name="_IntermediateCopyBlazorApplicationAssemblies"
-    Inputs="@(_BlazorResolvedRuntimeDependencies)"
-    Outputs="@(_BlazorResolvedRuntimeDependencies->'$(BlazorIntermediateResolvedApplicationAssembliesOutputPath)%(FileName)%(Extension)')">
-
-    <Copy
-      SourceFiles="@(_BlazorResolvedRuntimeDependencies)"
-      DestinationFiles="@(_BlazorResolvedRuntimeDependencies->'$(BlazorIntermediateResolvedApplicationAssembliesOutputPath)%(FileName)%(Extension)')"
-      SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
-      OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
-      Retries="$(CopyRetryCount)"
-      RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
-      UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)"
-      UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" />
-
-  </Target>
-
-  <Target
-    Name="_TouchBlazorApplicationAssemblies"
-    Inputs="$(BlazorBuildLinkerSwitchInputsCache)"
-    Outputs="@(_IntermediateResolvedRuntimeDependencies)">
-
-    <Touch Files="@(_IntermediateResolvedRuntimeDependencies)" ForceTouch="true" />
-
-  </Target>
-
-  <!--
-  Final part of the build pipeline:
-
-  * Collect the blazor application assemblies to be copied to the output and create a marker file.
-  * Call our CLI tool to generate the boot json if the list of assemblies has changed.
-  -->
-
-  <Target Name="_ResolveBlazorBootJsonInputs">
-    <ItemGroup>
-      <BlazorBootJsonInput Include="$(Configuration)" />
-      <BlazorBootJsonInput Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->'%(FullPath)')" />
-      <BlazorBootJsonInput Include="@(BlazorItemOutput->WithMetadataValue('Type','Pdb')->'%(FullPath)')" />
-      <BlazorBootJsonInput Include="@(_BlazorLinkingOption)" />
-      <BlazorBootJsonInput Include="$(BlazorEnableDebugging)" />
-    </ItemGroup>
-
-    <WriteLinesToFile
-      File="$(BlazorBuildBootJsonInputsCache)"
-      Lines="@(BlazorBootJsonInput)"
-      Overwrite="true"
-      WriteOnlyWhenDifferent="True" />
-
-    <ItemGroup>
-      <FileWrites Include="$(BlazorBuildBootJsonInputsCache)" />
-    </ItemGroup>
-
-  </Target>
+  <UsingTask TaskName="GenerateBlazorBootJson" AssemblyFile="$(BlazorTasksPath)" />
 
   <Target
     Name="_GenerateBlazorBootJson"
-    DependsOnTargets="_ResolveBlazorBootJsonInputs"
-    Inputs="$(BlazorBuildBootJsonInputsCache);@(_BlazorDependencyInput)"
+    Inputs="@(BlazorOutputWithTargetPath)"
     Outputs="$(BlazorBootJsonIntermediateOutputPath)">
     <ItemGroup>
-      <_UnlinkedAppReferencesPaths Include="@(_BlazorDependencyInput)" />
-      <_AppReferences Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->WithMetadataValue('PrimaryOutput','')->'%(FileName)%(Extension)')" />
-      <_AppReferences Include="@(BlazorItemOutput->WithMetadataValue('Type','Pdb')->'%(FileName)%(Extension)')" Condition="'$(BlazorEnableDebugging)' == 'true'" />
+      <_AppReferences Include="@(BlazorOutputWithTargetPath->WithMetadataValue('Extension','.dll'))" />
+      <_AppReferences Include="@(BlazorOutputWithTargetPath->WithMetadataValue('Extension','.pdb'))" Condition="'$(BlazorEnableDebugging)' == 'true'" />
     </ItemGroup>
-    <PropertyGroup>
-      <_LinkerEnabledFlag Condition="'$(_BlazorShouldLinkApplicationAssemblies)' != ''">--linker-enabled</_LinkerEnabledFlag>
-      <_ReferencesArg Condition="'@(_AppReferences)' != ''">--references &quot;$(BlazorBootJsonReferencesFilePath)&quot;</_ReferencesArg>
-      <_EmbeddedResourcesArg Condition="'@(_UnlinkedAppReferencesPaths)' != ''">--embedded-resources &quot;$(BlazorEmbeddedResourcesConfigFilePath)&quot;</_EmbeddedResourcesArg>
-    </PropertyGroup>
 
-    <WriteLinesToFile
-      File="$(BlazorBootJsonReferencesFilePath)"
-      Lines="@(_AppReferences)"
-      Overwrite="true" />
-
-    <WriteLinesToFile
-      Condition="'@(_UnlinkedAppReferencesPaths)' != ''"
-      File="$(BlazorEmbeddedResourcesConfigFilePath)"
-      Lines="@(_UnlinkedAppReferencesPaths)"
-      Overwrite="true" />
-
-    <Exec Command="$(BlazorBuildExe) write-boot-json &quot;@(IntermediateAssembly)&quot; $(_ReferencesArg) $(_EmbeddedResourcesArg) $(_LinkerEnabledFlag) --output &quot;$(BlazorBootJsonIntermediateOutputPath)&quot;" />
-
-    <ItemGroup Condition="Exists('$(BlazorBootJsonIntermediateOutputPath)')">
-      <_BlazorBootJson Include="$(BlazorBootJsonIntermediateOutputPath)" />
-      <_BlazorBootJsonEmbeddedContentFile Include="$(BlazorBootJsonIntermediateOutputDir)_content\**\*.*" />
-      <BlazorItemOutput Include="@(_BlazorBootJson)">
-        <TargetOutputPath>$(TargetDir)$(BlazorBootJsonOutputPath)</TargetOutputPath>
-        <Type>BootJson</Type>
-      </BlazorItemOutput>
-      <BlazorItemOutput Include="@(_BlazorBootJsonEmbeddedContentFile)">
-        <TargetOutputPath>$(TargetDir)dist/_content/%(RecursiveDir)%(FileName)%(Extension)</TargetOutputPath>
-      </BlazorItemOutput>
+    <GenerateBlazorBootJson
+      AssemblyPath="@(IntermediateAssembly)"
+      References="@(_AppReferences)"
+      LinkerEnabled="$(BlazorLinkOnBuild)"
+      OutputPath="$(BlazorBootJsonIntermediateOutputPath)" />
+
+    <ItemGroup>
+      <BlazorOutputWithTargetPath Include="$(BlazorBootJsonIntermediateOutputPath)" TargetOutputPath="$(BaseBlazorRuntimeOutputPath)$(BlazorBootJsonName)" />
       <FileWrites Include="$(BlazorBootJsonIntermediateOutputPath)" />
-      <FileWrites Include="@(_BlazorBootJsonEmbeddedContentFile)" />
     </ItemGroup>
-
   </Target>
 
 </Project>

+ 13 - 5
src/Components/Blazor/Build/src/targets/Publish.targets

@@ -26,9 +26,8 @@
       </ContentWithTargetPath>
 
       <!-- Publish all the 'dist' files -->
-      <_BlazorGCTPDIDistFiles Include="@(BlazorItemOutput->'%(TargetOutputPath)')" />
-      <_BlazorGCTPDI Include="@(_BlazorGCTPDIDistFiles)">
-        <TargetPath>$(BlazorPublishDistDir)$([MSBuild]::MakeRelative('$(TargetDir)dist\', %(Identity)))</TargetPath>
+      <_BlazorGCTPDI Include="%(BlazorOutputWithTargetPath.Identity)">
+        <TargetPath>$(AssemblyName)\%(TargetOutputPath)</TargetPath>
       </_BlazorGCTPDI>
 
       <ContentWithTargetPath Include="@(_BlazorGCTPDI)">
@@ -41,8 +40,17 @@
     <PropertyGroup>
       <_BlazorConfigPath>$(OutDir)$(AssemblyName).blazor.config</_BlazorConfigPath>
     </PropertyGroup>
-    <WriteLinesToFile File="$(_BlazorConfigPath)" Lines="." Overwrite="true" />
-    <WriteLinesToFile File="$(_BlazorConfigPath)" Lines="$(AssemblyName)/" Overwrite="false" />
+
+    <ItemGroup>
+      <_BlazorPublishConfigContent Include="." />
+      <_BlazorPublishConfigContent Include="$(AssemblyName)/" />
+    </ItemGroup>
+
+    <WriteLinesToFile
+      File="$(_BlazorConfigPath)"
+      Lines="@(_BlazorPublishConfigContent)"
+      Overwrite="true"
+      WriteOnlyWhenDifferent="true" />
   </Target>
 
   <!-- The following target runs only for standalone publishing -->

+ 37 - 0
src/Components/Blazor/Build/src/targets/StaticWebAssets.targets

@@ -0,0 +1,37 @@
+<Project>
+
+  <PropertyGroup>
+    <ResolveStaticWebAssetsInputsDependsOn>
+      $(ResolveStaticWebAssetsInputsDependsOn);
+      _RemoveBlazorCurrentProjectAssetsFromStaticWebAssets;
+    </ResolveStaticWebAssetsInputsDependsOn>
+
+    <GetCurrentProjectStaticWebAssetsDependsOn>
+      $(GetCurrentProjectStaticWebAssetsDependsOn);
+      _RemoveBlazorCurrentProjectAssetsFromStaticWebAssets;
+    </GetCurrentProjectStaticWebAssetsDependsOn>
+  </PropertyGroup>
+
+
+  <Target Name="_RemoveBlazorCurrentProjectAssetsFromStaticWebAssets">
+    <ItemGroup>
+      <StaticWebAsset Remove="@(StaticWebAsset)" Condition="'%(SourceType)' == ''" />
+    </ItemGroup>
+  </Target>
+
+  <Target Name="BlazorStaticWebAssetsComputeFilesToPublish"
+    AfterTargets="_StaticWebAssetsComputeFilesToPublish">
+
+    <ItemGroup>
+      <!-- We need to update the external static web assets to follow the blazor publish output convention that puts them inside $(TargetName)/dist instead of wwwroot -->
+      <_StandaloneExternalPublishStaticWebAsset Include="@(_ExternalPublishStaticWebAsset)" Condition="'%(RelativePath)' != ''">
+        <RelativePath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$([MSBuild]::NormalizePath('$([System.Text.RegularExpressions.Regex]::Replace('%(RelativePath)','^wwwroot\\?\/?(.*)','$(BlazorPublishDistDir)$1'))'))'))</RelativePath>
+      </_StandaloneExternalPublishStaticWebAsset>
+
+      <!-- Update doesn't work inside targets so we need to remove the items and re-add them. See https://github.com/microsoft/msbuild/issues/2835 for details -->
+      <ResolvedFileToPublish Remove="@(_StandaloneExternalPublishStaticWebAsset)" />
+      <ResolvedFileToPublish Include="@(_StandaloneExternalPublishStaticWebAsset)" />
+
+    </ItemGroup>
+  </Target>
+</Project>

+ 38 - 0
src/Components/Blazor/Build/test/BlazorCreateRootDescriptorFileTest.cs

@@ -0,0 +1,38 @@
+// 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 Xunit;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    public class BlazorCreateRootDescriptorFileTest
+    {
+        [Fact]
+        public void ProducesRootDescriptor()
+        {
+            // Arrange/Act
+            using var stream = new MemoryStream();
+
+            // Act
+            BlazorCreateRootDescriptorFile.WriteRootDescriptor(
+                stream,
+                new[] { "MyApp.dll" });
+
+            // Assert
+            stream.Position = 0;
+            var document = XDocument.Load(stream);
+            var rootElement = document.Root;
+
+            var assemblyElement = Assert.Single(rootElement.Elements());
+            Assert.Equal("assembly", assemblyElement.Name.ToString());
+            Assert.Equal("MyApp.dll", assemblyElement.Attribute("fullname").Value);
+
+            var typeElement = Assert.Single(assemblyElement.Elements());
+            Assert.Equal("type", typeElement.Name.ToString());
+            Assert.Equal("*", typeElement.Attribute("fullname").Value);
+            Assert.Equal("true", typeElement.Attribute("required").Value);
+        }
+    }
+}

+ 21 - 42
src/Components/Blazor/Build/test/BootJsonWriterTest.cs

@@ -1,62 +1,41 @@
 // 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 Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System;
-using System.Linq;
+using System.IO;
+using System.Text.Json;
+using System.Threading.Tasks;
 using Xunit;
 
-namespace Microsoft.AspNetCore.Blazor.Build.Test
+namespace Microsoft.AspNetCore.Blazor.Build
 {
     public class BootJsonWriterTest
     {
         [Fact]
-        public void ProducesJsonReferencingAssemblyAndDependencies()
+        public async Task ProducesJsonReferencingAssemblyAndDependencies()
         {
             // Arrange/Act
-            var assemblyReferences = new string[] { "System.Abc.dll", "MyApp.ClassLib.dll", };
-            var content = BootJsonWriter.GetBootJsonContent(
+            var assemblyReferences = new string[] { "MyApp.EntryPoint.dll", "System.Abc.dll", "MyApp.ClassLib.dll", };
+            using var stream = new MemoryStream();
+
+            // Act
+            GenerateBlazorBootJson.WriteBootJson(
+                stream,
                 "MyApp.Entrypoint.dll",
-                "MyNamespace.MyType::MyMethod",
                 assemblyReferences,
-                Enumerable.Empty<EmbeddedResourceInfo>(),
                 linkerEnabled: true);
 
             // Assert
-            var parsedContent = JsonConvert.DeserializeObject<JObject>(content);
-            Assert.Equal("MyApp.Entrypoint.dll", parsedContent["main"].Value<string>());
-            Assert.Equal("MyNamespace.MyType::MyMethod", parsedContent["entryPoint"].Value<string>());
-            Assert.Equal(assemblyReferences, parsedContent["assemblyReferences"].Values<string>());
-        }
-
-        [Fact]
-        public void IncludesReferencesToEmbeddedContent()
-        {
-            // Arrange/Act
-            var embeddedContent = new[]
+            stream.Position = 0;
+            using var parsedContent = await JsonDocument.ParseAsync(stream);
+            var rootElement = parsedContent.RootElement;
+            Assert.Equal("MyApp.Entrypoint.dll", rootElement.GetProperty("entryAssembly").GetString());
+            var assembliesElement = rootElement.GetProperty("assemblies");
+            Assert.Equal(assemblyReferences.Length, assembliesElement.GetArrayLength());
+            for (var i = 0; i < assemblyReferences.Length; i++)
             {
-                new EmbeddedResourceInfo(EmbeddedResourceKind.Static, "my/static/file"),
-                new EmbeddedResourceInfo(EmbeddedResourceKind.Css, "css/first.css"),
-                new EmbeddedResourceInfo(EmbeddedResourceKind.JavaScript, "javascript/first.js"),
-                new EmbeddedResourceInfo(EmbeddedResourceKind.Css, "css/second.css"),
-                new EmbeddedResourceInfo(EmbeddedResourceKind.JavaScript, "javascript/second.js"),
-            };
-            var content = BootJsonWriter.GetBootJsonContent(
-                "MyApp.Entrypoint",
-                "MyNamespace.MyType::MyMethod",
-                assemblyReferences: new[] { "Something.dll" },
-                embeddedContent: embeddedContent,
-                linkerEnabled: true);
-
-            // Assert
-            var parsedContent = JsonConvert.DeserializeObject<JObject>(content);
-            Assert.Equal(
-                new[] { "css/first.css", "css/second.css" },
-                parsedContent["cssReferences"].Values<string>());
-            Assert.Equal(
-                new[] { "javascript/first.js", "javascript/second.js" },
-                parsedContent["jsReferences"].Values<string>());
+                Assert.Equal(assemblyReferences[i], assembliesElement[i].GetString());
+            }
+            Assert.True(rootElement.GetProperty("linkerEnabled").GetBoolean());
         }
     }
 }

+ 950 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/Assert.cs

@@ -0,0 +1,950 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    internal class Assert : Xunit.Assert
+    {
+        // Matches `{filename}: error {code}: {message} [{project}]
+        // See https://stackoverflow.com/questions/3441452/msbuild-and-ignorestandarderrorwarningformat/5180353#5180353
+        private static readonly Regex ErrorRegex = new Regex(@"^(?'location'.+): error (?'errorcode'[A-Z0-9]+): (?'message'.+) \[(?'project'.+)\]$");
+        private static readonly Regex WarningRegex = new Regex(@"^(?'location'.+): warning (?'errorcode'[A-Z0-9]+): (?'message'.+) \[(?'project'.+)\]$");
+        private static readonly string[] AllowedBuildWarnings = new[]
+        {
+            "MSB3491" , // The process cannot access the file. As long as the build succeeds, we're ok.
+            "NETSDK1071", // "A PackageReference to 'Microsoft.NETCore.App' specified a Version ..."
+        };
+
+        public static void BuildPassed(MSBuildResult result, bool allowWarnings = false)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            if (result.ExitCode != 0)
+            {
+                throw new BuildFailedException(result);
+            }
+
+            var buildWarnings = GetBuildWarnings(result)
+                .Where(m => !AllowedBuildWarnings.Contains(m.match.Groups["errorcode"].Value))
+                .Select(m => m.line);
+
+            if (!allowWarnings && buildWarnings.Any())
+            {
+                throw new BuildWarningsException(result, buildWarnings);
+            }
+        }
+
+        public static void BuildError(MSBuildResult result, string errorCode, string location = null)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug.
+            var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+            for (var i = 0; i < lines.Length; i++)
+            {
+                var line = lines[i];
+                var match = ErrorRegex.Match(line);
+                if (match.Success)
+                {
+                    if (match.Groups["errorcode"].Value != errorCode)
+                    {
+                        continue;
+                    }
+
+                    if (location != null && match.Groups["location"].Value.Trim() != location)
+                    {
+                        continue;
+                    }
+
+                    // This is a match
+                    return;
+                }
+            }
+
+            throw new BuildErrorMissingException(result, errorCode, location);
+        }
+
+        public static void BuildWarning(MSBuildResult result, string errorCode, string location = null)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug.
+            foreach (var (_, match) in GetBuildWarnings(result))
+            {
+                if (match.Groups["errorcode"].Value != errorCode)
+                {
+                    continue;
+                }
+
+                if (location != null && match.Groups["location"].Value.Trim() != location)
+                {
+                    continue;
+                }
+
+                // This is a match
+                return;
+            }
+
+            throw new BuildErrorMissingException(result, errorCode, location);
+        }
+
+        private static IEnumerable<(string line, Match match)> GetBuildWarnings(MSBuildResult result)
+        {
+            var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+            for (var i = 0; i < lines.Length; i++)
+            {
+                var line = lines[i];
+                var match = WarningRegex.Match(line);
+                if (match.Success)
+                {
+                    yield return (line, match);
+                }
+            }
+        }
+
+        public static void BuildFailed(MSBuildResult result)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            };
+
+            if (result.ExitCode == 0)
+            {
+                throw new BuildPassedException(result);
+            }
+        }
+
+        public static void BuildOutputContainsLine(MSBuildResult result, string match)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            if (match == null)
+            {
+                throw new ArgumentNullException(nameof(match));
+            }
+
+            // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug.
+            var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+            for (var i = 0; i < lines.Length; i++)
+            {
+                var line = lines[i].Trim();
+                if (line == match)
+                {
+                    return;
+                }
+            }
+
+            throw new BuildOutputMissingException(result, match);
+        }
+
+        public static void BuildOutputDoesNotContainLine(MSBuildResult result, string match)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            if (match == null)
+            {
+                throw new ArgumentNullException(nameof(match));
+            }
+
+            // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug.
+            var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+            for (var i = 0; i < lines.Length; i++)
+            {
+                var line = lines[i].Trim();
+                if (line == match)
+                {
+                    throw new BuildOutputContainsLineException(result, match);
+                }
+            }
+        }
+
+        public static void FileContains(MSBuildResult result, string filePath, string match)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            filePath = Path.Combine(result.Project.DirectoryPath, filePath);
+            FileExists(result, filePath);
+
+            var text = File.ReadAllText(filePath);
+            if (text.Contains(match))
+            {
+                return;
+            }
+
+            throw new FileContentMissingException(result, filePath, File.ReadAllText(filePath), match);
+        }
+
+        public static void FileDoesNotContain(MSBuildResult result, string filePath, string match)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            filePath = Path.Combine(result.Project.DirectoryPath, filePath);
+            FileExists(result, filePath);
+
+            var text = File.ReadAllText(filePath);
+            if (text.Contains(match))
+            {
+                throw new FileContentFoundException(result, filePath, File.ReadAllText(filePath), match);
+            }
+        }
+
+        public static void FileContentEquals(MSBuildResult result, string filePath, string expected)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            filePath = Path.Combine(result.Project.DirectoryPath, filePath);
+            FileExists(result, filePath);
+
+            var actual = File.ReadAllText(filePath);
+            if (!actual.Equals(expected, StringComparison.Ordinal))
+            {
+                throw new FileContentNotEqualException(result, filePath, expected, actual);
+            }
+        }
+
+        public static void FileEquals(MSBuildResult result, string expected, string actual)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            expected = Path.Combine(result.Project.DirectoryPath, expected);
+            actual = Path.Combine(result.Project.DirectoryPath, actual);
+            FileExists(result, expected);
+            FileExists(result, actual);
+
+            if (!Enumerable.SequenceEqual(File.ReadAllBytes(expected), File.ReadAllBytes(actual)))
+            {
+                throw new FilesNotEqualException(result, expected, actual);
+            }
+        }
+
+        public static void FileContainsLine(MSBuildResult result, string filePath, string match)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            filePath = Path.Combine(result.Project.DirectoryPath, filePath);
+            FileExists(result, filePath);
+
+            var lines = File.ReadAllLines(filePath);
+            for (var i = 0; i < lines.Length; i++)
+            {
+                var line = lines[i].Trim();
+                if (line == match)
+                {
+                    return;
+                }
+            }
+
+            throw new FileContentMissingException(result, filePath, File.ReadAllText(filePath), match);
+        }
+
+        public static void FileDoesNotContainLine(MSBuildResult result, string filePath, string match)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            filePath = Path.Combine(result.Project.DirectoryPath, filePath);
+            FileExists(result, filePath);
+
+            var lines = File.ReadAllLines(filePath);
+            for (var i = 0; i < lines.Length; i++)
+            {
+                var line = lines[i].Trim();
+                if (line == match)
+                {
+                    throw new FileContentFoundException(result, filePath, File.ReadAllText(filePath), match);
+                }
+            }
+        }
+
+        public static string FileExists(MSBuildResult result, params string[] paths)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            var filePath = Path.Combine(result.Project.DirectoryPath, Path.Combine(paths));
+            if (!File.Exists(filePath))
+            {
+                throw new FileMissingException(result, filePath);
+            }
+
+            return filePath;
+        }
+
+        public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            if (directoryPath == null)
+            {
+                throw new ArgumentNullException(nameof(directoryPath));
+            }
+
+            if (searchPattern == null)
+            {
+                throw new ArgumentNullException(nameof(searchPattern));
+            }
+
+            directoryPath = Path.Combine(result.Project.DirectoryPath, directoryPath);
+
+            if (Directory.Exists(directoryPath))
+            {
+                var files = Directory.GetFiles(directoryPath, searchPattern, SearchOption.AllDirectories);
+                if (files.Length != expected)
+                {
+                    throw new FileCountException(result, expected, directoryPath, searchPattern, files);
+                }
+            }
+            else if (expected > 0)
+            {
+                // directory doesn't exist, that's OK if we expected to find nothing.
+                throw new FileCountException(result, expected, directoryPath, searchPattern, Array.Empty<string>());
+            }
+        }
+
+        public static void FileDoesNotExist(MSBuildResult result, params string[] paths)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            var filePath = Path.Combine(result.Project.DirectoryPath, Path.Combine(paths));
+            if (File.Exists(filePath))
+            {
+                throw new FileFoundException(result, filePath);
+            }
+        }
+
+        public static void NuspecContains(MSBuildResult result, string nuspecPath, string expected)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            if (nuspecPath == null)
+            {
+                throw new ArgumentNullException(nameof(nuspecPath));
+            }
+
+            if (expected == null)
+            {
+                throw new ArgumentNullException(nameof(expected));
+            }
+
+            nuspecPath = Path.Combine(result.Project.DirectoryPath, nuspecPath);
+            FileExists(result, nuspecPath);
+
+            var content = File.ReadAllText(nuspecPath);
+            if (!content.Contains(expected))
+            {
+                throw new NuspecException(result, nuspecPath, content, expected);
+            }
+        }
+
+        public static void NuspecDoesNotContain(MSBuildResult result, string nuspecPath, string expected)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            if (nuspecPath == null)
+            {
+                throw new ArgumentNullException(nameof(nuspecPath));
+            }
+
+            if (expected == null)
+            {
+                throw new ArgumentNullException(nameof(expected));
+            }
+
+            nuspecPath = Path.Combine(result.Project.DirectoryPath, nuspecPath);
+            FileExists(result, nuspecPath);
+
+            var content = File.ReadAllText(nuspecPath);
+            if (content.Contains(expected))
+            {
+                throw new NuspecFoundException(result, nuspecPath, content, expected);
+            }
+        }
+
+        // This method extracts the nupkg to a fixed directory path. To avoid the extra work of
+        // cleaning up after each invocation, this method accepts multiple files.
+        public static void NupkgContains(MSBuildResult result, string nupkgPath, params string[] filePaths)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            if (nupkgPath == null)
+            {
+                throw new ArgumentNullException(nameof(nupkgPath));
+            }
+
+            if (filePaths == null)
+            {
+                throw new ArgumentNullException(nameof(filePaths));
+            }
+
+            nupkgPath = Path.Combine(result.Project.DirectoryPath, nupkgPath);
+            FileExists(result, nupkgPath);
+
+            var unzipped = Path.Combine(result.Project.DirectoryPath, Path.GetFileNameWithoutExtension(nupkgPath));
+            ZipFile.ExtractToDirectory(nupkgPath, unzipped);
+
+            foreach (var filePath in filePaths)
+            {
+                if (!File.Exists(Path.Combine(unzipped, filePath)))
+                {
+                    throw new NupkgFileMissingException(result, nupkgPath, filePath);
+                }
+            }
+        }
+
+        // This method extracts the nupkg to a fixed directory path. To avoid the extra work of
+        // cleaning up after each invocation, this method accepts multiple files.
+        public static void NupkgDoesNotContain(MSBuildResult result, string nupkgPath, params string[] filePaths)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            if (nupkgPath == null)
+            {
+                throw new ArgumentNullException(nameof(nupkgPath));
+            }
+
+            if (filePaths == null)
+            {
+                throw new ArgumentNullException(nameof(filePaths));
+            }
+
+            nupkgPath = Path.Combine(result.Project.DirectoryPath, nupkgPath);
+            FileExists(result, nupkgPath);
+
+            var unzipped = Path.Combine(result.Project.DirectoryPath, Path.GetFileNameWithoutExtension(nupkgPath));
+            ZipFile.ExtractToDirectory(nupkgPath, unzipped);
+
+            foreach (var filePath in filePaths)
+            {
+                if (File.Exists(Path.Combine(unzipped, filePath)))
+                {
+                    throw new NupkgFileFoundException(result, nupkgPath, filePath);
+                }
+            }
+        }
+
+        public static void AssemblyContainsType(MSBuildResult result, string assemblyPath, string fullTypeName)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath));
+
+            var typeNames = GetDeclaredTypeNames(assemblyPath);
+            Assert.Contains(fullTypeName, typeNames);
+        }
+
+        public static void AssemblyDoesNotContainType(MSBuildResult result, string assemblyPath, string fullTypeName)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath));
+
+            var typeNames = GetDeclaredTypeNames(assemblyPath);
+            Assert.DoesNotContain(fullTypeName, typeNames);
+        }
+
+        private static IEnumerable<string> GetDeclaredTypeNames(string assemblyPath)
+        {
+            using (var file = File.OpenRead(assemblyPath))
+            {
+                var peReader = new PEReader(file);
+                var metadataReader = peReader.GetMetadataReader();
+                return metadataReader.TypeDefinitions.Where(t => !t.IsNil).Select(t =>
+                {
+                    var type = metadataReader.GetTypeDefinition(t);
+                    return metadataReader.GetString(type.Namespace) + "." + metadataReader.GetString(type.Name);
+                }).ToArray();
+            }
+        }
+
+        public static void AssemblyHasAttribute(MSBuildResult result, string assemblyPath, string fullTypeName)
+        {
+            if (result == null)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+            assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath));
+
+            var typeNames = GetAssemblyAttributes(assemblyPath);
+            Assert.Contains(fullTypeName, typeNames);
+        }
+
+        private static IEnumerable<string> GetAssemblyAttributes(string assemblyPath)
+        {
+            using (var file = File.OpenRead(assemblyPath))
+            {
+                var peReader = new PEReader(file);
+                var metadataReader = peReader.GetMetadataReader();
+                return metadataReader.CustomAttributes.Where(t => !t.IsNil).Select(t =>
+                {
+                    var attribute = metadataReader.GetCustomAttribute(t);
+                    var constructor = metadataReader.GetMemberReference((MemberReferenceHandle)attribute.Constructor);
+                    var type = metadataReader.GetTypeReference((TypeReferenceHandle)constructor.Parent);
+
+                    return metadataReader.GetString(type.Namespace) + "." + metadataReader.GetString(type.Name);
+                }).ToArray();
+            }
+        }
+
+        private abstract class MSBuildXunitException : Xunit.Sdk.XunitException
+        {
+            protected MSBuildXunitException(MSBuildResult result)
+            {
+                Result = result;
+            }
+
+            protected abstract string Heading { get; }
+
+            public MSBuildResult Result { get; }
+
+            public override string Message
+            {
+                get
+                {
+                    var message = new StringBuilder();
+                    message.AppendLine(Heading);
+                    message.Append(Result.FileName);
+                    message.Append(" ");
+                    message.Append(Result.Arguments);
+                    message.AppendLine();
+                    message.AppendLine();
+                    message.Append(Result.Output);
+                    message.AppendLine();
+                    message.Append("Exit Code:");
+                    message.Append(Result.ExitCode);
+                    return message.ToString();
+                }
+            }
+        }
+
+        private class BuildErrorMissingException : MSBuildXunitException
+        {
+            public BuildErrorMissingException(MSBuildResult result, string errorCode, string location)
+                : base(result)
+            {
+                ErrorCode = errorCode;
+                Location = location;
+            }
+
+            public string ErrorCode { get; }
+
+            public string Location { get; }
+
+            protected override string Heading
+            {
+                get
+                {
+                    return
+                        $"Error code '{ErrorCode}' was not found." + Environment.NewLine +
+                        $"Looking for '{Location ?? ".*"}: error {ErrorCode}: .*'";
+                }
+            }
+        }
+
+        private class BuildFailedException : MSBuildXunitException
+        {
+            public BuildFailedException(MSBuildResult result)
+                : base(result)
+            {
+            }
+
+            protected override string Heading => "Build failed.";
+        }
+
+        private class BuildWarningsException : MSBuildXunitException
+        {
+            public BuildWarningsException(MSBuildResult result, IEnumerable<string> warnings)
+                : base(result)
+            {
+                Warnings = warnings.ToList();
+            }
+
+            public List<string> Warnings { get; }
+
+            protected override string Heading => "Build contains unexpected warnings: " + string.Join(Environment.NewLine, Warnings);
+        }
+
+        private class BuildPassedException : MSBuildXunitException
+        {
+            public BuildPassedException(MSBuildResult result)
+                : base(result)
+            {
+            }
+
+            protected override string Heading => "Build should have failed, but it passed.";
+        }
+
+        private class BuildOutputMissingException : MSBuildXunitException
+        {
+            public BuildOutputMissingException(MSBuildResult result, string match)
+                : base(result)
+            {
+                Match = match;
+            }
+
+            public string Match { get; }
+
+            protected override string Heading => $"Build did not contain the line: '{Match}'.";
+        }
+
+        private class BuildOutputContainsLineException : MSBuildXunitException
+        {
+            public BuildOutputContainsLineException(MSBuildResult result, string match)
+                : base(result)
+            {
+                Match = match;
+            }
+
+            public string Match { get; }
+
+            protected override string Heading => $"Build output contains the line: '{Match}'.";
+        }
+
+        private class FileContentFoundException : MSBuildXunitException
+        {
+            public FileContentFoundException(MSBuildResult result, string filePath, string content, string match)
+                : base(result)
+            {
+                FilePath = filePath;
+                Content = content;
+                Match = match;
+            }
+
+            public string Content { get; }
+
+            public string FilePath { get; }
+
+            public string Match { get; }
+
+            protected override string Heading
+            {
+                get
+                {
+                    var builder = new StringBuilder();
+                    builder.AppendFormat("File content of '{0}' should not contain line: '{1}'.", FilePath, Match);
+                    builder.AppendLine();
+                    builder.AppendLine();
+                    builder.AppendLine(Content);
+                    return builder.ToString();
+                }
+            }
+        }
+
+        private class FileContentMissingException : MSBuildXunitException
+        {
+            public FileContentMissingException(MSBuildResult result, string filePath, string content, string match)
+                : base(result)
+            {
+                FilePath = filePath;
+                Content = content;
+                Match = match;
+            }
+
+            public string Content { get; }
+
+            public string FilePath { get; }
+
+            public string Match { get; }
+
+            protected override string Heading
+            {
+                get
+                {
+                    var builder = new StringBuilder();
+                    builder.AppendFormat("File content of '{0}' did not contain the line: '{1}'.", FilePath, Match);
+                    builder.AppendLine();
+                    builder.AppendLine();
+                    builder.AppendLine(Content);
+                    return builder.ToString();
+                }
+            }
+        }
+
+        private class FileContentNotEqualException : MSBuildXunitException
+        {
+            public FileContentNotEqualException(MSBuildResult result, string filePath, string expected, string actual)
+                : base(result)
+            {
+                FilePath = filePath;
+                Expected = expected;
+                Actual = actual;
+            }
+
+            public string Actual { get; }
+
+            public string FilePath { get; }
+
+            public string Expected { get; }
+
+            protected override string Heading
+            {
+                get
+                {
+                    var builder = new StringBuilder();
+                    builder.AppendFormat("File content of '{0}' did not match the expected content: '{1}'.", FilePath, Expected);
+                    builder.AppendLine();
+                    builder.AppendLine();
+                    builder.AppendLine(Actual);
+                    return builder.ToString();
+                }
+            }
+        }
+
+        private class FilesNotEqualException : MSBuildXunitException
+        {
+            public FilesNotEqualException(MSBuildResult result, string expected, string actual)
+                : base(result)
+            {
+                Expected = expected;
+                Actual = actual;
+            }
+
+            public string Actual { get; }
+
+            public string Expected { get; }
+
+            protected override string Heading
+            {
+                get
+                {
+                    var builder = new StringBuilder();
+                    builder.AppendFormat("File content of '{0}' did not match the contents of '{1}'.", Expected, Actual);
+                    builder.AppendLine();
+                    builder.AppendLine();
+                    builder.AppendLine(Actual);
+                    return builder.ToString();
+                }
+            }
+        }
+
+        private class FileMissingException : MSBuildXunitException
+        {
+            public FileMissingException(MSBuildResult result, string filePath)
+                : base(result)
+            {
+                FilePath = filePath;
+            }
+
+            public string FilePath { get; }
+
+            protected override string Heading => $"File: '{FilePath}' was not found.";
+        }
+
+        private class FileCountException : MSBuildXunitException
+        {
+            public FileCountException(MSBuildResult result, int expected, string directoryPath, string searchPattern, string[] files)
+                : base(result)
+            {
+                Expected = expected;
+                DirectoryPath = directoryPath;
+                SearchPattern = searchPattern;
+                Files = files;
+            }
+
+            public string DirectoryPath { get; }
+
+            public int Expected { get; }
+
+            public string[] Files { get; }
+
+            public string SearchPattern { get; }
+
+            protected override string Heading
+            {
+                get
+                {
+                    var heading = new StringBuilder();
+                    heading.AppendLine($"Expected {Expected} files matching {SearchPattern} in {DirectoryPath}, found {Files.Length}.");
+
+                    if (Files.Length > 0)
+                    {
+                        heading.AppendLine("Files:");
+
+                        foreach (var file in Files)
+                        {
+                            heading.Append("\t");
+                            heading.Append(file);
+                        }
+
+                        heading.AppendLine();
+                    }
+
+                    return heading.ToString();
+                }
+            }
+        }
+
+        private class FileFoundException : MSBuildXunitException
+        {
+            public FileFoundException(MSBuildResult result, string filePath)
+                : base(result)
+            {
+                FilePath = filePath;
+            }
+
+            public string FilePath { get; }
+
+            protected override string Heading => $"File: '{FilePath}' was found, but should not exist.";
+        }
+
+        private class NuspecException : MSBuildXunitException
+        {
+            public NuspecException(MSBuildResult result, string filePath, string content, string expected)
+                : base(result)
+            {
+                FilePath = filePath;
+                Content = content;
+                Expected = expected;
+            }
+
+            public string Content { get; }
+
+            public string Expected { get; }
+
+            public string FilePath { get; }
+
+            protected override string Heading
+            {
+                get
+                {
+                    return
+                        $"nuspec: '{FilePath}' did not contain the expected content." + Environment.NewLine +
+                        Environment.NewLine +
+                        $"expected: {Expected}" + Environment.NewLine +
+                        Environment.NewLine +
+                        $"actual: {Content}";
+                }
+            }
+        }
+
+        private class NuspecFoundException : MSBuildXunitException
+        {
+            public NuspecFoundException(MSBuildResult result, string filePath, string content, string expected)
+                : base(result)
+            {
+                FilePath = filePath;
+                Content = content;
+                Expected = expected;
+            }
+
+            public string Content { get; }
+
+            public string Expected { get; }
+
+            public string FilePath { get; }
+
+            protected override string Heading
+            {
+                get
+                {
+                    return
+                        $"nuspec: '{FilePath}' should not contain the content {Expected}." +
+                        Environment.NewLine +
+                        $"actual content: {Content}";
+                }
+            }
+        }
+
+        private class NupkgFileMissingException : MSBuildXunitException
+        {
+            public NupkgFileMissingException(MSBuildResult result, string nupkgPath, string filePath)
+                : base(result)
+            {
+                NupkgPath = nupkgPath;
+                FilePath = filePath;
+            }
+
+            public string FilePath { get; }
+
+            public string NupkgPath { get; }
+
+            protected override string Heading => $"File: '{FilePath}' was not found was not found in {NupkgPath}.";
+        }
+
+        private class NupkgFileFoundException : MSBuildXunitException
+        {
+            public NupkgFileFoundException(MSBuildResult result, string nupkgPath, string filePath)
+                : base(result)
+            {
+                NupkgPath = nupkgPath;
+                FilePath = filePath;
+            }
+
+            public string FilePath { get; }
+
+            public string NupkgPath { get; }
+
+            protected override string Heading => $"File: '{FilePath}' was found in {NupkgPath}.";
+        }
+    }
+}

+ 40 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/BuildIncrementalismTest.cs

@@ -0,0 +1,40 @@
+// 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.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    public class BuildIncrementalismTest
+    {
+        [Fact]
+        public async Task Build_WithLinker_IsIncremental()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("standalone");
+            var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+            Assert.BuildPassed(result);
+
+            var buildOutputDirectory = project.BuildOutputDirectory;
+
+            // Act
+            var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, project.BuildOutputDirectory);
+
+            // Assert
+            for (var i = 0; i < 3; i++)
+            {
+                result = await MSBuildProcessManager.DotnetMSBuild(project);
+                Assert.BuildPassed(result);
+
+                var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, project.BuildOutputDirectory);
+                Assert.Equal(thumbPrint.Count, newThumbPrint.Count);
+                for (var j = 0; j < thumbPrint.Count; j++)
+                {
+                    Assert.Equal(thumbPrint[j], newThumbPrint[j]);
+                }
+            }
+        }
+    }
+}

+ 74 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs

@@ -0,0 +1,74 @@
+// 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.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    public class BuildIntegrationTest
+    {
+        [Fact]
+        public async Task Build_WithDefaultSettings_Works()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("standalone");
+            var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+            Assert.BuildPassed(result);
+
+            var buildOutputDirectory = project.BuildOutputDirectory;
+
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "blazor.boot.json");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "blazor.webassembly.js");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "wasm", "mono.wasm");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "wasm", "mono.js");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "standalone.dll");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+        }
+
+        [Fact]
+        public async Task Build_Hosted_Works()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", });
+            project.TargetFramework = "netcoreapp5.0";
+            var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+            Assert.BuildPassed(result);
+
+            var buildOutputDirectory = project.BuildOutputDirectory;
+            var blazorConfig = Path.Combine(buildOutputDirectory, "standalone.blazor.config");
+            Assert.FileExists(result, blazorConfig);
+
+            var path = Path.GetFullPath(Path.Combine(project.SolutionPath, "standalone", "bin", project.Configuration, "netstandard2.1", "standalone.dll"));
+            Assert.FileContains(result, blazorConfig, path);
+            Assert.FileDoesNotExist(result, buildOutputDirectory, "dist", "_framework", "_bin", "standalone.dll");
+        }
+
+        [Fact]
+        public async Task Build_WithLinkOnBuildDisabled_Works()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("standalone");
+            project.AddProjectFileContent(
+@"<PropertyGroup>
+    <BlazorLinkOnBuild>false</BlazorLinkOnBuild>
+</PropertyGroup>");
+
+            var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+            Assert.BuildPassed(result);
+
+            var buildOutputDirectory = project.BuildOutputDirectory;
+
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "blazor.boot.json");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "blazor.webassembly.js");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "wasm", "mono.wasm");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "wasm", "mono.js");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "standalone.dll");
+            Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+        }
+    }
+}

+ 74 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/FileThumbPrint.cs

@@ -0,0 +1,74 @@
+// 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.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    internal class FileThumbPrint : IEquatable<FileThumbPrint>
+    {
+        private FileThumbPrint(string path, DateTime lastWriteTimeUtc, string hash)
+        {
+            FilePath = path;
+            LastWriteTimeUtc = lastWriteTimeUtc;
+            Hash = hash;
+        }
+
+        public string FilePath { get; }
+
+        public DateTime LastWriteTimeUtc { get; }
+
+        public string Hash { get; }
+
+        public override string ToString()
+        {
+            return $"{Path.GetFileName(FilePath)}, {LastWriteTimeUtc.ToString("u")}, {Hash}";
+        }
+
+        /// <summary>
+        /// Returns a list of thumbprints for all files (recursive) in the specified directory, sorted by file paths.
+        /// </summary>
+        public static List<FileThumbPrint> CreateFolderThumbprint(ProjectDirectory project, string directoryPath, params string[] filesToIgnore)
+        {
+            directoryPath = Path.Combine(project.DirectoryPath, directoryPath);
+            var files = Directory.GetFiles(directoryPath).Where(p => !filesToIgnore.Contains(p));
+            var thumbprintLookup = new List<FileThumbPrint>();
+            foreach (var file in files)
+            {
+                var thumbprint = Create(file);
+                thumbprintLookup.Add(thumbprint);
+            }
+
+            thumbprintLookup.Sort(Comparer<FileThumbPrint>.Create((a, b) => StringComparer.Ordinal.Compare(a.FilePath, b.FilePath)));
+            return thumbprintLookup;
+        }
+
+        public static FileThumbPrint Create(string path)
+        {
+            byte[] hashBytes;
+            using (var sha1 = SHA1.Create())
+            using (var fileStream = File.OpenRead(path))
+            {
+                hashBytes = sha1.ComputeHash(fileStream);
+            }
+
+            var hash = Convert.ToBase64String(hashBytes);
+            var lastWriteTimeUtc = File.GetLastWriteTimeUtc(path);
+            return new FileThumbPrint(path, lastWriteTimeUtc, hash);
+        }
+
+        public bool Equals(FileThumbPrint other)
+        {
+            return
+                string.Equals(FilePath, other.FilePath, StringComparison.Ordinal) &&
+                LastWriteTimeUtc == other.LastWriteTimeUtc &&
+                string.Equals(Hash, other.Hash, StringComparison.Ordinal);
+        }
+
+        public override int GetHashCode() => LastWriteTimeUtc.GetHashCode();
+    }
+}

+ 282 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/MSBuildProcessManager.cs

@@ -0,0 +1,282 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.CommandLineUtils;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    internal static class MSBuildProcessManager
+    {
+        public static Task<MSBuildResult> DotnetMSBuild(
+            ProjectDirectory project,
+            string target = "Build",
+            string args = null)
+        {
+            var buildArgumentList = new List<string>
+            {
+                // Disable node-reuse. We don't want msbuild processes to stick around
+                // once the test is completed.
+                "/nr:false",
+
+                // Always generate a bin log for debugging purposes
+                "/bl",
+            };
+
+            buildArgumentList.Add($"/t:{target}");
+            buildArgumentList.Add($"/p:Configuration={project.Configuration}");
+            buildArgumentList.Add(args);
+
+            var buildArguments = string.Join(" ", buildArgumentList);
+
+            return MSBuildProcessManager.RunProcessAsync(project, buildArguments);
+        }
+
+        public static async Task<MSBuildResult> RunProcessAsync(
+            ProjectDirectory project,
+            string arguments,
+            TimeSpan? timeout = null)
+        {
+            var processStartInfo = new ProcessStartInfo()
+            {
+                WorkingDirectory = project.DirectoryPath,
+                UseShellExecute = false,
+                RedirectStandardError = true,
+                RedirectStandardOutput = true,
+            };
+
+            processStartInfo.FileName = DotNetMuxer.MuxerPathOrDefault();
+            processStartInfo.Arguments = $"msbuild {arguments}";
+
+            // Suppresses the 'Welcome to .NET Core!' output that times out tests and causes locked file issues.
+            // When using dotnet we're not guarunteed to run in an environment where the dotnet.exe has had its first run experience already invoked.
+            processStartInfo.EnvironmentVariables["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "true";
+
+            var processResult = await RunProcessCoreAsync(processStartInfo, timeout);
+
+            return new MSBuildResult(project, processResult.FileName, processResult.Arguments, processResult.ExitCode, processResult.Output);
+        }
+
+        internal static Task<ProcessResult> RunProcessCoreAsync(
+            ProcessStartInfo processStartInfo,
+            TimeSpan? timeout = null)
+        {
+            timeout = timeout ?? TimeSpan.FromSeconds(5 * 60);
+
+            var process = new Process()
+            {
+                StartInfo = processStartInfo,
+                EnableRaisingEvents = true,
+            };
+
+            var output = new StringBuilder();
+            var outputLock = new object();
+
+            var diagnostics = new StringBuilder();
+            diagnostics.AppendLine("Process execution diagnostics:");
+
+            process.ErrorDataReceived += Process_ErrorDataReceived;
+            process.OutputDataReceived += Process_OutputDataReceived;
+
+            process.Start();
+            process.BeginOutputReadLine();
+            process.BeginErrorReadLine();
+
+            var timeoutTask = GetTimeoutForProcess(process, timeout, diagnostics);
+
+            var waitTask = Task.Run(() =>
+            {
+                // We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
+                // this code used Process.Exited, which could result in us missing some output due to the ordering of
+                // events.
+                //
+                // See the remarks here: https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx
+                if (!process.WaitForExit(Int32.MaxValue))
+                {
+                    // unreachable - the timeoutTask will kill the process before this happens.
+                    throw new TimeoutException();
+                }
+
+                process.WaitForExit();
+
+
+                string outputString;
+                lock (outputLock)
+                {
+                    // This marks the end of the diagnostic info which we collect when something goes wrong.
+                    diagnostics.AppendLine("Process output:");
+
+                    // Expected output
+                    // Process execution diagnostics:
+                    // ...
+                    // Process output:
+                    outputString = diagnostics.ToString();
+                    outputString += output.ToString();
+                }
+
+                var result = new ProcessResult(process.StartInfo.FileName, process.StartInfo.Arguments, process.ExitCode, outputString);
+                return result;
+            });
+
+            return Task.WhenAny<ProcessResult>(waitTask, timeoutTask).Unwrap();
+
+            void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
+            {
+                lock (outputLock)
+                {
+                    output.AppendLine(e.Data);
+                }
+            }
+
+            void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
+            {
+                lock (outputLock)
+                {
+                    output.AppendLine(e.Data);
+                }
+            }
+
+            async Task<ProcessResult> GetTimeoutForProcess(Process process, TimeSpan? timeout, StringBuilder diagnostics)
+            {
+                await Task.Delay(timeout.Value);
+
+                // Don't timeout during debug sessions
+                while (Debugger.IsAttached)
+                {
+                    Thread.Sleep(TimeSpan.FromSeconds(1));
+                }
+                if (!process.HasExited)
+                {
+                    await CollectDumps(process, timeout, diagnostics);
+
+                    // This is a timeout.
+                    process.Kill();
+                }
+
+                throw new TimeoutException($"command '${process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {timeout}. Output: {output.ToString()}");
+            }
+
+            static async Task CollectDumps(Process process, TimeSpan? timeout, StringBuilder diagnostics)
+            {
+                var procDumpProcess = await CaptureDump(process, timeout, diagnostics);
+                var allDotNetProcesses = Process.GetProcessesByName("dotnet");
+
+                var allDotNetChildProcessCandidates = allDotNetProcesses
+                    .Where(p => p.StartTime >= process.StartTime && p.Id != process.Id);
+
+                if (!allDotNetChildProcessCandidates.Any())
+                {
+                    diagnostics.AppendLine("Couldn't find any candidate child process.");
+                    foreach (var dotnetProcess in allDotNetProcesses)
+                    {
+                        diagnostics.AppendLine($"Found dotnet process with PID {dotnetProcess.Id} and start time {dotnetProcess.StartTime}.");
+                    }
+                }
+
+                foreach (var childProcess in allDotNetChildProcessCandidates)
+                {
+                    diagnostics.AppendLine($"Found child process candidate '{childProcess.Id}'.");
+                }
+
+                var childrenProcessDumpProcesses = await Task.WhenAll(allDotNetChildProcessCandidates.Select(d => CaptureDump(d, timeout, diagnostics)));
+                foreach (var childProcess in childrenProcessDumpProcesses)
+                {
+                    if (childProcess != null && childProcess.HasExited)
+                    {
+                        diagnostics.AppendLine($"ProcDump failed to run for child dotnet process candidate '{process.Id}'.");
+                        childProcess.Kill();
+                    }
+                }
+
+                if (procDumpProcess != null && procDumpProcess.HasExited)
+                {
+                    diagnostics.AppendLine($"ProcDump failed to run for '{process.Id}'.");
+                    procDumpProcess.Kill();
+                }
+            }
+
+            static async Task<Process> CaptureDump(Process process, TimeSpan? timeout, StringBuilder diagnostics)
+            {
+                var metadataAttributes = Assembly.GetExecutingAssembly()
+                    .GetCustomAttributes<AssemblyMetadataAttribute>();
+
+                var procDumpPath = metadataAttributes
+                    .SingleOrDefault(ama => ama.Key == "ProcDumpToolPath")?.Value;
+
+                if (string.IsNullOrEmpty(procDumpPath))
+                {
+                    diagnostics.AppendLine("ProcDumpPath not defined.");
+                    return null;
+                }
+                var procDumpExePath = Path.Combine(procDumpPath, "procdump.exe");
+                if (!File.Exists(procDumpExePath))
+                {
+                    diagnostics.AppendLine($"Can't find procdump.exe in '{procDumpPath}'.");
+                    return null;
+                }
+
+                var dumpDirectory = metadataAttributes
+                    .SingleOrDefault(ama => ama.Key == "ArtifactsLogDir")?.Value;
+
+                if (string.IsNullOrEmpty(dumpDirectory))
+                {
+                    diagnostics.AppendLine("ArtifactsLogDir not defined.");
+                    return null;
+                }
+
+                if (!Directory.Exists(dumpDirectory))
+                {
+                    diagnostics.AppendLine($"'{dumpDirectory}' does not exist.");
+                    return null;
+                }
+
+                var procDumpPattern = Path.Combine(dumpDirectory, "HangingProcess_PROCESSNAME_PID_YYMMDD_HHMMSS.dmp");
+                var procDumpStartInfo = new ProcessStartInfo(
+                    procDumpExePath,
+                    $"-accepteula -ma {process.Id} {procDumpPattern}")
+                {
+                    CreateNoWindow = true,
+                    UseShellExecute = false,
+                    RedirectStandardOutput = true,
+                    RedirectStandardError = true
+                };
+
+                var procDumpProcess = Process.Start(procDumpStartInfo);
+                var tcs = new TaskCompletionSource<object>();
+
+                procDumpProcess.Exited += (s, a) => tcs.TrySetResult(null);
+                procDumpProcess.EnableRaisingEvents = true;
+
+                await Task.WhenAny(tcs.Task, Task.Delay(timeout ?? TimeSpan.FromSeconds(30)));
+                return procDumpProcess;
+            }
+        }
+
+        internal class ProcessResult
+        {
+            public ProcessResult(string fileName, string arguments, int exitCode, string output)
+            {
+                FileName = fileName;
+                Arguments = arguments;
+                ExitCode = exitCode;
+                Output = output;
+            }
+
+            public string Arguments { get; }
+
+            public string FileName { get; }
+
+            public int ExitCode { get; }
+
+            public string Output { get; }
+        }
+    }
+}

+ 28 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/MSBuildResult.cs

@@ -0,0 +1,28 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    internal class MSBuildResult
+    {
+        public MSBuildResult(ProjectDirectory project, string fileName, string arguments, int exitCode, string output)
+        {
+            Project = project;
+            FileName = fileName;
+            Arguments = arguments;
+            ExitCode = exitCode;
+            Output = output;
+        }
+
+        public ProjectDirectory Project { get; }
+
+        public string Arguments { get; }
+
+        public string FileName { get; }
+
+        public int ExitCode { get; }
+
+        public string Output { get; }
+    }
+
+}

+ 211 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/ProjectDirectory.cs

@@ -0,0 +1,211 @@
+// 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.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    internal class ProjectDirectory : IDisposable
+    {
+        public bool PreserveWorkingDirectory { get; set; } = false;
+
+        private static readonly string RepoRoot = GetTestAttribute("Testing.RepoRoot");
+
+        public static ProjectDirectory Create(string projectName, string baseDirectory = "", string[] additionalProjects = null)
+        {
+            var destinationPath = Path.Combine(Path.GetTempPath(), "BlazorBuild", baseDirectory, Path.GetRandomFileName());
+            Directory.CreateDirectory(destinationPath);
+
+            try
+            {
+                if (Directory.EnumerateFiles(destinationPath).Any())
+                {
+                    throw new InvalidOperationException($"{destinationPath} should be empty");
+                }
+
+                if (string.IsNullOrEmpty(RepoRoot))
+                {
+                    throw new InvalidOperationException("RepoRoot was not specified.");
+                }
+
+                var testAppsRoot = Path.Combine(RepoRoot, "src", "Components", "Blazor", "Build", "testassets");
+                foreach (var project in new string[] { projectName, }.Concat(additionalProjects ?? Array.Empty<string>()))
+                {
+                    var projectRoot = Path.Combine(testAppsRoot, project);
+                    if (!Directory.Exists(projectRoot))
+                    {
+                        throw new InvalidOperationException($"Could not find project at '{projectRoot}'");
+                    }
+
+                    var projectDestination = Path.Combine(destinationPath, project);
+                    var projectDestinationDir = Directory.CreateDirectory(projectDestination);
+                    CopyDirectory(new DirectoryInfo(projectRoot), projectDestinationDir);
+                    SetupDirectoryBuildFiles(RepoRoot, testAppsRoot, projectDestination);
+                }
+
+                var directoryPath = Path.Combine(destinationPath, projectName);
+                var projectPath = Path.Combine(directoryPath, projectName + ".csproj");
+
+                CopyRepositoryAssets(destinationPath);
+
+                return new ProjectDirectory(
+                    destinationPath,
+                    directoryPath,
+                    projectPath);
+            }
+            catch
+            {
+                CleanupDirectory(destinationPath);
+                throw;
+            }
+
+            static void CopyDirectory(DirectoryInfo source, DirectoryInfo destination, bool recursive = true)
+            {
+                foreach (var file in source.EnumerateFiles())
+                {
+                    file.CopyTo(Path.Combine(destination.FullName, file.Name));
+                }
+
+                if (!recursive)
+                {
+                    return;
+                }
+
+                foreach (var directory in source.EnumerateDirectories())
+                {
+                    if (directory.Name == "bin")
+                    {
+                        // Just in case someone has opened the project in an IDE or built it. We don't want to copy
+                        // these folders.
+                        continue;
+                    }
+
+                    var created = destination.CreateSubdirectory(directory.Name);
+                    if (directory.Name == "obj")
+                    {
+                        // Copy NuGet restore assets (viz all the files at the root of the obj directory, but stop there.)
+                        CopyDirectory(directory, created, recursive: false);
+                    }
+                    else
+                    {
+                        CopyDirectory(directory, created);
+                    }
+                }
+            }
+
+            static void SetupDirectoryBuildFiles(string repoRoot, string testAppsRoot, string projectDestination)
+            {
+                var beforeDirectoryPropsContent =
+$@"<Project>
+  <PropertyGroup>
+    <RepoRoot>{repoRoot}</RepoRoot>
+  </PropertyGroup>
+</Project>";
+                File.WriteAllText(Path.Combine(projectDestination, "Before.Directory.Build.props"), beforeDirectoryPropsContent);
+
+                new List<string> { "Directory.Build.props", "Directory.Build.targets", }
+                    .ForEach(file =>
+                    {
+                        var source = Path.Combine(testAppsRoot, file);
+                        var destination = Path.Combine(projectDestination, file);
+                        File.Copy(source, destination);
+                    });
+            }
+
+            static void CopyRepositoryAssets(string projectRoot)
+            {
+                const string GlobalJsonFileName = "global.json";
+                var globalJsonPath = Path.Combine(RepoRoot, GlobalJsonFileName);
+
+                var destinationFile = Path.Combine(projectRoot, GlobalJsonFileName);
+                File.Copy(globalJsonPath, destinationFile);
+            }
+        }
+
+        protected ProjectDirectory(string solutionPath, string directoryPath, string projectFilePath)
+        {
+            SolutionPath = solutionPath;
+            DirectoryPath = directoryPath;
+            ProjectFilePath = projectFilePath;
+        }
+
+        public string DirectoryPath { get; }
+
+        public string ProjectFilePath { get; }
+
+        public string SolutionPath { get; }
+
+        public string TargetFramework { get; set; } = "netstandard2.1";
+
+#if DEBUG
+        public string Configuration => "Debug";
+#elif RELEASE
+        public string Configuration => "Release";
+#else
+#error Configuration not supported
+#endif
+
+        public string IntermediateOutputDirectory => Path.Combine("obj", Configuration, TargetFramework);
+
+        public string BuildOutputDirectory => Path.Combine("bin", Configuration, TargetFramework);
+
+        public string PublishOutputDirectory => Path.Combine(BuildOutputDirectory, "publish");
+
+        internal void AddProjectFileContent(string content)
+        {
+            if (content == null)
+            {
+                throw new ArgumentNullException(nameof(content));
+            }
+
+            var existing = File.ReadAllText(ProjectFilePath);
+            var updated = existing.Replace("<!-- Test Placeholder -->", content);
+            File.WriteAllText(ProjectFilePath, updated);
+        }
+
+        public void Dispose()
+        {
+            if (PreserveWorkingDirectory)
+            {
+                Console.WriteLine($"Skipping deletion of working directory {SolutionPath}");
+            }
+            else
+            {
+                CleanupDirectory(SolutionPath);
+            }
+        }
+
+        internal static void CleanupDirectory(string filePath)
+        {
+            var tries = 5;
+            var sleep = TimeSpan.FromSeconds(3);
+
+            for (var i = 0; i < tries; i++)
+            {
+                try
+                {
+                    Directory.Delete(filePath, recursive: true);
+                    return;
+                }
+                catch when (i < tries - 1)
+                {
+                    Console.WriteLine($"Failed to delete directory {filePath}, trying again.");
+                    Thread.Sleep(sleep);
+                }
+            }
+        }
+
+        private static string GetTestAttribute(string key)
+        {
+            return typeof(ProjectDirectory).Assembly
+                .GetCustomAttributes<AssemblyMetadataAttribute>()
+                .FirstOrDefault(f => f.Key == key)
+                ?.Value;
+        }
+    }
+}

+ 21 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/ProjectDirectoryTest.cs

@@ -0,0 +1,21 @@
+// 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 Xunit;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    public class ProjectDirectoryTest
+    {
+        [Fact]
+        public void ProjectDirectory_IsNotSetToBePreserved()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("standalone");
+
+            // Act & Assert
+            // This flag is only meant for local debugging and should not be set when checking in.
+            Assert.False(project.PreserveWorkingDirectory);
+        }
+    }
+}

+ 195 - 0
src/Components/Blazor/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs

@@ -0,0 +1,195 @@
+// 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.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Blazor.Build
+{
+    public class PublishIntegrationTest
+    {
+        [Fact]
+        public async Task Publish_WithDefaultSettings_Works()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("standalone", additionalProjects: new [] { "razorclasslibrary" });
+            var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+
+            Assert.BuildPassed(result);
+
+            var publishDirectory = project.PublishOutputDirectory;
+            var blazorPublishDirectory = Path.Combine(publishDirectory, Path.GetFileNameWithoutExtension(project.ProjectFilePath));
+
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.boot.json");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.webassembly.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.wasm");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "standalone.dll");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+
+            // Verify referenced static web assets
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_content", "RazorClassLibrary", "styles.css");
+
+            // Verify static assets are in the publish directory
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "index.html");
+
+            // Verify web.config
+            Assert.FileExists(result, publishDirectory, "web.config");
+        }
+
+        [Fact]
+        public async Task Publish_WithNoBuild_Works()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
+            var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build");
+
+            Assert.BuildPassed(result);
+
+            result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/p:NoBuild=true");
+
+            Assert.BuildPassed(result);
+
+            var publishDirectory = project.PublishOutputDirectory;
+            var blazorPublishDirectory = Path.Combine(publishDirectory, Path.GetFileNameWithoutExtension(project.ProjectFilePath));
+
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.boot.json");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.webassembly.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.wasm");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "standalone.dll");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+
+            // Verify static assets are in the publish directory
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "index.html");
+
+            // Verify static web assets from referenced projects are copied.
+            // Uncomment once https://github.com/aspnet/AspNetCore/issues/17426 is resolved.
+            // Assert.FileExists(result, blazorPublishDirectory, "dist", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
+            // Assert.FileExists(result, blazorPublishDirectory, "dist", "_content", "RazorClassLibrary", "styles.css");
+
+            // Verify static assets are in the publish directory
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "index.html");
+
+            // Verify web.config
+            Assert.FileExists(result, publishDirectory, "web.config");
+        }
+
+        [Fact]
+        public async Task Publish_WithLinkOnBuildDisabled_Works()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("standalone", additionalProjects: new [] { "razorclasslibrary" });
+            project.AddProjectFileContent(
+@"<PropertyGroup>
+    <BlazorLinkOnBuild>false</BlazorLinkOnBuild>
+</PropertyGroup>");
+
+            var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+
+            Assert.BuildPassed(result);
+
+            var publishDirectory = project.PublishOutputDirectory;
+            var blazorPublishDirectory = Path.Combine(publishDirectory, Path.GetFileNameWithoutExtension(project.ProjectFilePath));
+
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.boot.json");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.webassembly.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.wasm");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "standalone.dll");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+
+            // Verify static assets are in the publish directory
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "index.html");
+
+            // Verify referenced static web assets
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_content", "RazorClassLibrary", "styles.css");
+
+            // Verify web.config
+            Assert.FileExists(result, publishDirectory, "web.config");
+        }
+
+        [Fact]
+        public async Task Publish_HostedApp_Works()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", });
+            project.TargetFramework = "netcoreapp5.0";
+            var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+
+            Assert.BuildPassed(result);
+
+            var publishDirectory = project.PublishOutputDirectory;
+            // Make sure the main project exists
+            Assert.FileExists(result, publishDirectory, "blazorhosted.dll");
+
+            var blazorPublishDirectory = Path.Combine(publishDirectory, "standalone");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.boot.json");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.webassembly.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.wasm");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "standalone.dll");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+
+            // Verify static assets are in the publish directory
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "index.html");
+
+            // Verify static web assets from referenced projects are copied.
+            Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
+            Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css");
+
+            // Verify static assets are in the publish directory
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "index.html");
+
+            // Verify web.config
+            Assert.FileExists(result, publishDirectory, "web.config");
+
+            var blazorConfig = Path.Combine(result.Project.DirectoryPath, publishDirectory, "standalone.blazor.config");
+            var blazorConfigLines = File.ReadAllLines(blazorConfig);
+            Assert.Equal(".", blazorConfigLines[0]);
+            Assert.Equal("standalone/", blazorConfigLines[1]);
+        }
+
+        [Fact]
+        public async Task Publish_HostedApp_WithNoBuild_Works()
+        {
+            // Arrange
+            using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", });
+            project.TargetFramework = "netcoreapp5.0";
+            var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build");
+
+            Assert.BuildPassed(result);
+
+            result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/p:NoBuild=true");
+
+            var publishDirectory = project.PublishOutputDirectory;
+            // Make sure the main project exists
+            Assert.FileExists(result, publishDirectory, "blazorhosted.dll");
+
+            var blazorPublishDirectory = Path.Combine(publishDirectory, "standalone");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.boot.json");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "blazor.webassembly.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.wasm");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "wasm", "mono.js");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "standalone.dll");
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+
+            // Verify static assets are in the publish directory
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "index.html");
+
+            // Verify static web assets from referenced projects are copied.
+            // Uncomment once https://github.com/aspnet/AspNetCore/issues/17426 is resolved.
+            // Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
+            // Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css");
+
+            // Verify static assets are in the publish directory
+            Assert.FileExists(result, blazorPublishDirectory, "dist", "index.html");
+
+            // Verify web.config
+            Assert.FileExists(result, publishDirectory, "web.config");
+        }
+    }
+}

+ 26 - 1
src/Components/Blazor/Build/test/Microsoft.AspNetCore.Blazor.Build.Tests.csproj

@@ -5,6 +5,7 @@
 
     <!-- Exclude the TestFiles directory from default wildcards -->
     <DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
+    <BuildHelixPayload>false</BuildHelixPayload>
   </PropertyGroup>
 
   <ItemGroup>
@@ -25,16 +26,40 @@
     <Reference Include="Microsoft.AspNetCore.Blazor.Mono" />
     <Reference Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" />
     <Reference Include="Microsoft.AspNetCore.Razor.Language" />
+    <Reference Include="Microsoft.Build.Framework" />
+    <Reference Include="Microsoft.Build.Utilities.Core" />
     <Reference Include="Microsoft.CodeAnalysis.Razor" />
+    <Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
   </ItemGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\testassets\StandaloneApp\StandaloneApp.csproj" />
     <Compile Include="$(SharedSourceRoot)test\SkipOnHelixAttribute.cs" />
-
     <Compile Include="$(ComponentsSharedSourceRoot)test\**\*.cs" LinkBase="Helpers" />
   </ItemGroup>
 
+  <ItemGroup>
+    <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
+      <_Parameter1>Testing.RepoRoot</_Parameter1>
+      <_Parameter2>$(RepoRoot)</_Parameter2>
+    </AssemblyAttribute>
+  </ItemGroup>
+
+  <Target Name="RestoreTestAssets" AfterTargets="Restore;Build" Condition="'$(DotNetBuildFromSource)' != 'true'">
+    <ItemGroup>
+      <_TestAsset Include="..\testassets\standalone\standalone.csproj" />
+      <_TestAsset Include="..\testassets\blazorhosted\blazorhosted.csproj" />
+    </ItemGroup>
+
+    <MSBuild
+      Projects="@(_TestAsset)"
+      Targets="Restore"
+      Properties="
+        RepoRoot=$(RepoRoot);
+        MicrosoftNetCompilersToolsetPackageVersion=$(MicrosoftNetCompilersToolsetPackageVersion);
+        MicrosoftNETSdkRazorPackageVersion=$(MicrosoftNETSdkRazorPackageVersion)" />
+  </Target>
+
   <!-- A bit of msbuild magic to support reference resolver tests -->
   <Target Name="CreateReferenceHintPathsList" AfterTargets="Build">
     <ItemGroup>

+ 2 - 2
src/Components/Blazor/Build/test/RuntimeDependenciesResolverTest.cs

@@ -9,7 +9,7 @@ using System.Text;
 using Microsoft.AspNetCore.Testing;
 using Xunit;
 
-namespace Microsoft.AspNetCore.Blazor.Build.Test
+namespace Microsoft.AspNetCore.Blazor.Build
 {
     public class RuntimeDependenciesResolverTest
     {
@@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
 
             // Act
 
-            var paths = RuntimeDependenciesResolver
+            var paths = ResolveBlazorRuntimeDependencies
                 .ResolveRuntimeDependenciesCore(
                     mainAssemblyLocation,
                     references,

+ 31 - 0
src/Components/Blazor/Build/testassets/Directory.Build.props

@@ -0,0 +1,31 @@
+<Project>
+    <Import Project="Before.Directory.Build.props" Condition="Exists('Before.Directory.Build.props')" />
+
+    <PropertyGroup>
+      <RepoRoot Condition="'$(RepoRoot)' ==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), global.json))\</RepoRoot>
+      <ComponentsRoot>$(RepoRoot)src\Components\</ComponentsRoot>
+      <BlazorBuildRoot>$(ComponentsRoot)Blazor\Build\src\</BlazorBuildRoot>
+      <ReferenceBlazorBuildFromSourceProps>$(BlazorBuildRoot)ReferenceBlazorBuildFromSource.props</ReferenceBlazorBuildFromSourceProps>
+
+      <!-- Workaround for https://github.com/aspnet/AspNetCore/issues/17308 -->
+      <DefaultNetCoreTargetFramework>netcoreapp3.1</DefaultNetCoreTargetFramework>
+
+      <EnableSourceLink>false</EnableSourceLink>
+      <DeterministicSourcePaths>false</DeterministicSourcePaths>
+    </PropertyGroup>
+
+    <Import Project="$(RepoRoot)eng\Versions.props" />
+
+    <ItemGroup>
+      <!-- Use the sample compiler \ SDK that the rest of our build uses-->
+      <PackageReference Include="Microsoft.Net.Compilers.Toolset"
+        Version="$(MicrosoftNetCompilersToolsetPackageVersion)"
+        PrivateAssets="all"
+        IsImplicitlyDefined="true" />
+
+      <PackageReference Include="Microsoft.NET.Sdk.Razor"
+        Version="$(MicrosoftNETSdkRazorPackageVersion)"
+        PrivateAssets="All"
+        IsImplicitlyDefined="true" />
+    </ItemGroup>
+</Project>

+ 2 - 0
src/Components/Blazor/Build/testassets/Directory.Build.targets

@@ -0,0 +1,2 @@
+<Project>
+</Project>

+ 15 - 0
src/Components/Blazor/Build/testassets/blazorhosted/Program.cs

@@ -0,0 +1,15 @@
+using System;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+
+namespace blazorhosted.Server
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            Console.WriteLine(typeof(IWebHost));
+        }
+    }
+}

+ 12 - 0
src/Components/Blazor/Build/testassets/blazorhosted/blazorhosted.csproj

@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp5.0</TargetFramework>
+    <DisableImplicitComponentsAnalyzers>true</DisableImplicitComponentsAnalyzers>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\standalone\standalone.csproj" />
+  </ItemGroup>
+
+</Project>

+ 8 - 0
src/Components/Blazor/Build/testassets/razorclasslibrary/RazorClassLibrary.csproj

@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.Razor">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.1</TargetFramework>
+    <RazorLangVersion>3.0</RazorLangVersion>
+  </PropertyGroup>
+
+</Project>

+ 1 - 0
src/Components/Blazor/Build/testassets/razorclasslibrary/wwwroot/styles.css

@@ -0,0 +1 @@
+

+ 0 - 0
src/Components/Blazor/Build/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js


+ 8 - 0
src/Components/Blazor/Build/testassets/standalone/App.razor

@@ -0,0 +1,8 @@
+<Router AppAssembly="@typeof(Program).Assembly">
+    <Found Context="routeData">
+        <RouteView RouteData="@routeData"/>
+    </Found>
+    <NotFound>
+        <p>Sorry, there's nothing at this address.</p>
+    </NotFound>
+</Router>

+ 5 - 0
src/Components/Blazor/Build/testassets/standalone/Pages/Index.razor

@@ -0,0 +1,5 @@
+@page "/"
+
+<h1>Hello, world!</h1>
+
+Welcome to your new app.

+ 10 - 0
src/Components/Blazor/Build/testassets/standalone/Program.cs

@@ -0,0 +1,10 @@
+
+namespace standalone
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+        }
+    }
+}

+ 2 - 0
src/Components/Blazor/Build/testassets/standalone/_Imports.razor

@@ -0,0 +1,2 @@
+@using Microsoft.AspNetCore.Components.Routing
+@using standalone

+ 20 - 0
src/Components/Blazor/Build/testassets/standalone/standalone.csproj

@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+  <Import Project="$(ReferenceBlazorBuildFromSourceProps)" />
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.1</TargetFramework>
+    <RazorLangVersion>3.0</RazorLangVersion>
+  </PropertyGroup>
+
+  <!-- Test Placeholder -->
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.AspNetCore.Components" Version="3.0.0" />
+    <PackageReference Include="Microsoft.AspNetCore.Blazor.Mono" Version="$(MicrosoftAspNetCoreBlazorMonoPackageVersion)" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\razorclasslibrary\RazorClassLibrary.csproj" />
+  </ItemGroup>
+
+</Project>

+ 24 - 0
src/Components/Blazor/Build/testassets/standalone/wwwroot/index.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width" />
+    <title>standalone</title>
+    <base href="/" />
+    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
+    <link href="css/site.css" rel="stylesheet" />
+</head>
+
+<body>
+    <app>Loading...</app>
+
+    <div id="blazor-error-ui">
+        An unhandled error has occurred.
+        <a href="" class="reload">Reload</a>
+        <a class="dismiss">🗙</a>
+    </div>
+    <script src="_framework/blazor.webassembly.js"></script>
+</body>
+
+</html>

+ 1 - 0
src/Components/Blazor/Directory.Build.targets

@@ -4,4 +4,5 @@
   <PropertyGroup>
     <ComponentsPackageVersion>$(PackageVersion)</ComponentsPackageVersion>
   </PropertyGroup>
+
 </Project>

+ 0 - 10
src/Components/Blazor/Http/ref/Microsoft.AspNetCore.Blazor.HttpClient.csproj

@@ -1,10 +0,0 @@
-<!-- This file is automatically generated. -->
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFrameworks>netstandard2.0</TargetFrameworks>
-  </PropertyGroup>
-  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
-    <Compile Include="Microsoft.AspNetCore.Blazor.HttpClient.netstandard2.0.cs" />
-    <Reference Include="System.Text.Json"  />
-  </ItemGroup>
-</Project>

+ 0 - 18
src/Components/Blazor/Http/ref/Microsoft.AspNetCore.Blazor.HttpClient.netstandard2.0.cs

@@ -1,18 +0,0 @@
-// 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.
-
-namespace Microsoft.AspNetCore.Components
-{
-    public static partial class HttpClientJsonExtensions
-    {
-        [System.Diagnostics.DebuggerStepThroughAttribute]
-        public static System.Threading.Tasks.Task<T> GetJsonAsync<T>(this System.Net.Http.HttpClient httpClient, string requestUri) { throw null; }
-        public static System.Threading.Tasks.Task PostJsonAsync(this System.Net.Http.HttpClient httpClient, string requestUri, object content) { throw null; }
-        public static System.Threading.Tasks.Task<T> PostJsonAsync<T>(this System.Net.Http.HttpClient httpClient, string requestUri, object content) { throw null; }
-        public static System.Threading.Tasks.Task PutJsonAsync(this System.Net.Http.HttpClient httpClient, string requestUri, object content) { throw null; }
-        public static System.Threading.Tasks.Task<T> PutJsonAsync<T>(this System.Net.Http.HttpClient httpClient, string requestUri, object content) { throw null; }
-        public static System.Threading.Tasks.Task SendJsonAsync(this System.Net.Http.HttpClient httpClient, System.Net.Http.HttpMethod method, string requestUri, object content) { throw null; }
-        [System.Diagnostics.DebuggerStepThroughAttribute]
-        public static System.Threading.Tasks.Task<T> SendJsonAsync<T>(this System.Net.Http.HttpClient httpClient, System.Net.Http.HttpMethod method, string requestUri, object content) { throw null; }
-    }
-}

+ 1 - 0
src/Components/Blazor/Http/src/Microsoft.AspNetCore.Blazor.HttpClient.csproj

@@ -4,6 +4,7 @@
     <TargetFramework>netstandard2.0</TargetFramework>
     <Description>Provides experimental support for using System.Text.Json with HttpClient. Intended for use with Blazor running under WebAssembly.</Description>
     <IsShippingPackage>true</IsShippingPackage>
+    <HasReferenceAssembly>false</HasReferenceAssembly>
   </PropertyGroup>
 
   <ItemGroup>

+ 25 - 0
src/Components/Blazor/Mono.WebAssembly.Interop/src/InternalCalls.cs

@@ -0,0 +1,25 @@
+// 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.Runtime.CompilerServices;
+
+namespace WebAssembly.JSInterop
+{
+    /// <summary>
+    /// Methods that map to the functions compiled into the Mono WebAssembly runtime,
+    /// as defined by 'mono_add_internal_call' calls in driver.c
+    /// </summary>
+    internal class InternalCalls
+    {
+        // The exact namespace, type, and method names must match the corresponding entries
+        // in driver.c in the Mono distribution
+
+        // We're passing asyncHandle by ref not because we want it to be writable, but so it gets
+        // passed as a pointer (4 bytes). We can pass 4-byte values, but not 8-byte ones.
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern string InvokeJSMarshalled(out string exception, ref long asyncHandle, string functionIdentifier, string argsJson);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern TRes InvokeJSUnmarshalled<T0, T1, T2, TRes>(out string exception, string functionIdentifier, T0 arg0, T1 arg1, T2 arg2);
+    }
+}

+ 17 - 0
src/Components/Blazor/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj

@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.1</TargetFramework>
+    <Description>Abstractions and features for interop between Mono WebAssembly and JavaScript code.</Description>
+    <PackageTags>wasm;javascript;interop</PackageTags>
+    <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    <IsPackable>true</IsPackable>
+    <IsShipping>true</IsShipping>
+    <HasReferenceAssembly>false</HasReferenceAssembly>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Reference Include="Microsoft.JSInterop" />
+  </ItemGroup>
+
+</Project>

+ 157 - 0
src/Components/Blazor/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs

@@ -0,0 +1,157 @@
+// 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.Text.Json;
+using Microsoft.JSInterop;
+using Microsoft.JSInterop.Infrastructure;
+using WebAssembly.JSInterop;
+
+namespace Mono.WebAssembly.Interop
+{
+    /// <summary>
+    /// Provides methods for invoking JavaScript functions for applications running
+    /// on the Mono WebAssembly runtime.
+    /// </summary>
+    public class MonoWebAssemblyJSRuntime : JSInProcessRuntime
+    {
+        /// <summary>
+        /// Gets the <see cref="MonoWebAssemblyJSRuntime"/> used to perform operations using <see cref="DotNetDispatcher"/>.
+        /// </summary>
+        private static MonoWebAssemblyJSRuntime Instance { get; set; }
+
+        /// <summary>
+        /// Initializes the <see cref="MonoWebAssemblyJSRuntime"/> to be used to perform operations using <see cref="DotNetDispatcher"/>.
+        /// </summary>
+        /// <param name="jsRuntime">The <see cref="MonoWebAssemblyJSRuntime"/> instance.</param>
+        protected static void Initialize(MonoWebAssemblyJSRuntime jsRuntime)
+        {
+            if (Instance != null)
+            {
+                throw new InvalidOperationException("MonoWebAssemblyJSRuntime has already been initialized.");
+            }
+
+            Instance = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime));
+        }
+
+        /// <inheritdoc />
+        protected override string InvokeJS(string identifier, string argsJson)
+        {
+            var noAsyncHandle = default(long);
+            var result = InternalCalls.InvokeJSMarshalled(out var exception, ref noAsyncHandle, identifier, argsJson);
+            return exception != null
+                ? throw new JSException(exception)
+                : result;
+        }
+
+        /// <inheritdoc />
+        protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson)
+        {
+            InternalCalls.InvokeJSMarshalled(out _, ref asyncHandle, identifier, argsJson);
+        }
+
+        // Invoked via Mono's JS interop mechanism (invoke_method)
+        private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson)
+        {
+            var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), callId: null);
+            return DotNetDispatcher.Invoke(Instance, callInfo, argsJson);
+        }
+
+        // Invoked via Mono's JS interop mechanism (invoke_method)
+        private static void EndInvokeJS(string argsJson)
+            => DotNetDispatcher.EndInvokeJS(Instance, argsJson);
+
+        // Invoked via Mono's JS interop mechanism (invoke_method)
+        private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson)
+        {
+            // Figure out whether 'assemblyNameOrDotNetObjectId' is the assembly name or the instance ID
+            // We only need one for any given call. This helps to work around the limitation that we can
+            // only pass a maximum of 4 args in a call from JS to Mono WebAssembly.
+            string assemblyName;
+            long dotNetObjectId;
+            if (char.IsDigit(assemblyNameOrDotNetObjectId[0]))
+            {
+                dotNetObjectId = long.Parse(assemblyNameOrDotNetObjectId);
+                assemblyName = null;
+            }
+            else
+            {
+                dotNetObjectId = default;
+                assemblyName = assemblyNameOrDotNetObjectId;
+            }
+
+            var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId);
+            DotNetDispatcher.BeginInvokeDotNet(Instance, callInfo, argsJson);
+        }
+
+        protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNetInvocationResult dispatchResult)
+        {
+            // For failures, the common case is to call EndInvokeDotNet with the Exception object.
+            // For these we'll serialize as something that's useful to receive on the JS side.
+            // If the value is not an Exception, we'll just rely on it being directly JSON-serializable.
+            var resultOrError = dispatchResult.Success ? dispatchResult.Result : dispatchResult.Exception.ToString();
+
+            // We pass 0 as the async handle because we don't want the JS-side code to
+            // send back any notification (we're just providing a result for an existing async call)
+            var args = JsonSerializer.Serialize(new[] { callInfo.CallId, dispatchResult.Success, resultOrError }, JsonSerializerOptions);
+            BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args);
+        }
+
+        #region Custom MonoWebAssemblyJSRuntime methods
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <returns>The result of the function invocation.</returns>
+        public TRes InvokeUnmarshalled<TRes>(string identifier)
+            => InvokeUnmarshalled<object, object, object, TRes>(identifier, null, null, null);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        public TRes InvokeUnmarshalled<T0, TRes>(string identifier, T0 arg0)
+            => InvokeUnmarshalled<T0, object, object, TRes>(identifier, arg0, null, null);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="T1">The type of the second argument.</typeparam>
+        /// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <param name="arg1">The second argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        public TRes InvokeUnmarshalled<T0, T1, TRes>(string identifier, T0 arg0, T1 arg1)
+            => InvokeUnmarshalled<T0, T1, object, TRes>(identifier, arg0, arg1, null);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="T1">The type of the second argument.</typeparam>
+        /// <typeparam name="T2">The type of the third argument.</typeparam>
+        /// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <param name="arg1">The second argument.</param>
+        /// <param name="arg2">The third argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        public TRes InvokeUnmarshalled<T0, T1, T2, TRes>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+        {
+            var result = InternalCalls.InvokeJSUnmarshalled<T0, T1, T2, TRes>(out var exception, identifier, arg0, arg1, arg2);
+            return exception != null
+                ? throw new JSException(exception)
+                : result;
+        }
+
+        #endregion
+    }
+}

+ 0 - 16
src/Components/Blazor/Server/ref/Microsoft.AspNetCore.Blazor.Server.csproj

@@ -1,16 +0,0 @@
-<!-- This file is automatically generated. -->
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFrameworks>$(DefaultNetCoreTargetFramework)</TargetFrameworks>
-  </PropertyGroup>
-  <ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
-    <Compile Include="Microsoft.AspNetCore.Blazor.Server.netcoreapp.cs" />
-    <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions"  />
-    <Reference Include="Microsoft.AspNetCore.SpaServices.Extensions"  />
-    <Reference Include="Microsoft.AspNetCore.StaticFiles"  />
-    <Reference Include="Microsoft.AspNetCore.WebSockets"  />
-    <Reference Include="Microsoft.Extensions.FileProviders.Embedded"  />
-    <Reference Include="Newtonsoft.Json"  />
-    <Reference Include="Mono.Cecil"  />
-  </ItemGroup>
-</Project>

+ 0 - 22
src/Components/Blazor/Server/ref/Microsoft.AspNetCore.Blazor.Server.netcoreapp.cs

@@ -1,22 +0,0 @@
-// 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.
-
-namespace Microsoft.AspNetCore.Builder
-{
-    public static partial class BlazorHostingApplicationBuilderExtensions
-    {
-        public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseClientSideBlazorFiles(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, string clientAssemblyFilePath) { throw null; }
-        public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseClientSideBlazorFiles<TClientApp>(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) { throw null; }
-    }
-    public static partial class BlazorHostingEndpointRouteBuilderExtensions
-    {
-        public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToClientSideBlazor(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string clientAssemblyFilePath, string filePath) { throw null; }
-        public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToClientSideBlazor(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string clientAssemblyFilePath, string pattern, string filePath) { throw null; }
-        public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToClientSideBlazor<TClientApp>(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string filePath) { throw null; }
-        public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToClientSideBlazor<TClientApp>(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, string filePath) { throw null; }
-    }
-    public static partial class BlazorMonoDebugProxyAppBuilderExtensions
-    {
-        public static void UseBlazorDebugging(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) { }
-    }
-}

+ 2 - 1
src/Components/Blazor/Server/src/Microsoft.AspNetCore.Blazor.Server.csproj

@@ -4,13 +4,14 @@
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
     <Description>Runtime server features for ASP.NET Core Blazor applications.</Description>
     <IsShippingPackage>true</IsShippingPackage>
+    <HasReferenceAssembly>false</HasReferenceAssembly>
   </PropertyGroup>
 
   <ItemGroup>
     <Compile Include="$(ComponentsSharedSourceRoot)\src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" />
   </ItemGroup>
 
-  <ItemGroup>
+   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
     <Reference Include="Microsoft.AspNetCore.SpaServices.Extensions" />
     <Reference Include="Microsoft.AspNetCore.StaticFiles" />

+ 117 - 17
src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs

@@ -9,9 +9,9 @@ using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Runtime.InteropServices;
+using System.Text.Json;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Http;
-using Newtonsoft.Json;
 using WsProxy;
 
 namespace Microsoft.AspNetCore.Builder
@@ -21,6 +21,15 @@ namespace Microsoft.AspNetCore.Builder
     /// </summary>
     public static class BlazorMonoDebugProxyAppBuilderExtensions
     {
+        private static JsonSerializerOptions JsonOptions = new JsonSerializerOptions
+        {
+            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+            PropertyNameCaseInsensitive = true,
+            IgnoreNullValues = true
+        };
+
+        private static string DefaultDebuggerHost = "http://localhost:9222";
+
         /// <summary>
         /// Adds middleware for needed for debugging Blazor applications
         /// inside Chromium dev tools.
@@ -29,6 +38,8 @@ namespace Microsoft.AspNetCore.Builder
         {
             app.UseWebSockets();
 
+            app.UseVisualStudioDebuggerConnectionRequestHandlers();
+
             app.Use((context, next) =>
             {
                 var requestPath = context.Request.Path;
@@ -52,6 +63,85 @@ namespace Microsoft.AspNetCore.Builder
             });
         }
 
+        private static string GetDebuggerHost()
+        {
+            var envVar = Environment.GetEnvironmentVariable("ASPNETCORE_WEBASSEMBLYDEBUGHOST");
+
+            if (string.IsNullOrEmpty(envVar))
+            {
+                return DefaultDebuggerHost;
+            }
+            else
+            {
+                return envVar;
+            }
+        }
+
+        private static int GetDebuggerPort()
+        {
+            var host = GetDebuggerHost();
+            return new Uri(host).Port;
+        }
+
+        private static void UseVisualStudioDebuggerConnectionRequestHandlers(this IApplicationBuilder app)
+        {
+            // Unfortunately VS doesn't send any deliberately distinguishing information so we know it's
+            // not a regular browser or API client. The closest we can do is look for the *absence* of a
+            // User-Agent header. In the future, we should try to get VS to send a special header to indicate
+            // this is a debugger metadata request.
+            app.Use(async (context, next) =>
+            {
+                var request = context.Request;
+                var requestPath = request.Path;
+                if (requestPath.StartsWithSegments("/json")
+                    && !request.Headers.ContainsKey("User-Agent"))
+                {
+                    if (requestPath.Equals("/json", StringComparison.OrdinalIgnoreCase) || requestPath.Equals("/json/list", StringComparison.OrdinalIgnoreCase))
+                    {
+                        var availableTabs = await GetOpenedBrowserTabs();
+
+                        // Filter the list to only include tabs displaying the requested app,
+                        // but only during the "choose application to debug" phase. We can't apply
+                        // the same filter during the "connecting" phase (/json/list), nor do we need to.
+                        if (requestPath.Equals("/json"))
+                        {
+                            availableTabs = availableTabs.Where(tab => tab.Url.StartsWith($"{request.Scheme}://{request.Host}{request.PathBase}/"));
+                        }
+
+                        var proxiedTabInfos = availableTabs.Select(tab =>
+                        {
+                            var underlyingV8Endpoint = tab.WebSocketDebuggerUrl;
+                            var proxiedV8Endpoint = $"ws://{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
+                            return new
+                            {
+                                description = "",
+                                devtoolsFrontendUrl = "",
+                                id = tab.Id,
+                                title = tab.Title,
+                                type = tab.Type,
+                                url = tab.Url,
+                                webSocketDebuggerUrl = proxiedV8Endpoint
+                            };
+                        });
+
+                        context.Response.ContentType = "application/json";
+                        await context.Response.WriteAsync(JsonSerializer.Serialize(proxiedTabInfos));
+                    }
+                    else if (requestPath.Equals("/json/version", StringComparison.OrdinalIgnoreCase))
+                    {
+                        var browserVersionJson = await GetBrowserVersionInfoAsync();
+
+                        context.Response.ContentType = "application/json";
+                        await context.Response.WriteAsync(browserVersionJson);
+                    }
+                }
+                else
+                {
+                    await next();
+                }
+            });
+        }
+
         private static async Task DebugWebSocketProxyRequest(HttpContext context)
         {
             if (!context.WebSockets.IsWebSocketRequest)
@@ -81,13 +171,13 @@ namespace Microsoft.AspNetCore.Builder
 
             // TODO: Allow overriding port (but not hostname, as we're connecting to the
             // local browser, not to the webserver serving the app)
-            var debuggerHost = "http://localhost:9222";
+            var debuggerHost = GetDebuggerHost();
             var debuggerTabsListUrl = $"{debuggerHost}/json";
             IEnumerable<BrowserTab> availableTabs;
 
             try
             {
-                availableTabs = await GetOpenedBrowserTabs(debuggerHost);
+                availableTabs = await GetOpenedBrowserTabs();
             }
             catch (Exception ex)
             {
@@ -147,28 +237,30 @@ namespace Microsoft.AspNetCore.Builder
             var underlyingV8Endpoint = tabToDebug.WebSocketDebuggerUrl;
             var proxyEndpoint = $"{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
             var devToolsUrlAbsolute = new Uri(debuggerHost + tabToDebug.DevtoolsFrontendUrl);
-            var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?ws={proxyEndpoint}";
+            var wsParamName = request.IsHttps ? "wss" : "ws";
+            var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{wsParamName}={proxyEndpoint}";
             context.Response.Redirect(devToolsUrlWithProxy);
         }
 
         private static string GetLaunchChromeInstructions(string appRootUrl)
         {
-            var profilePath = Path.Combine(Path.GetTempPath(), "blazor-edge-debug");
+            var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug");
+            var debuggerPort = GetDebuggerPort();
 
             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
             {
                 return $@"<p>Press Win+R and enter the following:</p>
-                          <p><strong><code>chrome --remote-debugging-port=9222 --user-data-dir=""{profilePath}"" {appRootUrl}</code></strong></p>";
+                          <p><strong><code>chrome --remote-debugging-port={debuggerPort} --user-data-dir=""{profilePath}"" {appRootUrl}</code></strong></p>";
             }
             else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
             {
                 return $@"<p>In a terminal window execute the following:</p>
-                          <p><strong><code>google-chrome --remote-debugging-port=9222 --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
+                          <p><strong><code>google-chrome --remote-debugging-port={debuggerPort} --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
             }
             else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
             {
                 return $@"<p>Execute the following:</p>
-                          <p><strong><code>open /Applications/Google\ Chrome.app --args --remote-debugging-port=9222 --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
+                          <p><strong><code>open /Applications/Google\ Chrome.app --args --remote-debugging-port={debuggerPort} --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
             }
             else
             {
@@ -178,17 +270,18 @@ namespace Microsoft.AspNetCore.Builder
 
         private static string GetLaunchEdgeInstructions(string appRootUrl)
         {
-            var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug");
+            var profilePath = Path.Combine(Path.GetTempPath(), "blazor-edge-debug");
+            var debugggerPort = GetDebuggerPort();
 
             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
             {
                 return $@"<p>Press Win+R and enter the following:</p>
-                          <p><strong><code>msedge --remote-debugging-port=9222 --user-data-dir=""{profilePath}"" {appRootUrl}</code></strong></p>";
+                          <p><strong><code>msedge --remote-debugging-port={debugggerPort} --user-data-dir=""{profilePath}"" --no-first-run {appRootUrl}</code></strong></p>";
             }
             else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
             {
                 return $@"<p>In a terminal window execute the following:</p>
-                          <p><strong><code>open /Applications/Microsoft\ Edge\ Dev.app --args --remote-debugging-port=9222 --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
+                          <p><strong><code>open /Applications/Microsoft\ Edge\ Dev.app --args --remote-debugging-port={debugggerPort} --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
             }
             else
             {
@@ -196,17 +289,24 @@ namespace Microsoft.AspNetCore.Builder
             }
         }
 
-        private static async Task<IEnumerable<BrowserTab>> GetOpenedBrowserTabs(string debuggerHost)
+        private static async Task<string> GetBrowserVersionInfoAsync()
         {
-            using (var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) })
-            {
-                var jsonResponse = await httpClient.GetStringAsync($"{debuggerHost}/json");
-                return JsonConvert.DeserializeObject<BrowserTab[]>(jsonResponse);
-            }
+            using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
+            var debuggerHost = GetDebuggerHost();
+            return await httpClient.GetStringAsync($"{debuggerHost}/json/version");
+        }
+
+        private static async Task<IEnumerable<BrowserTab>> GetOpenedBrowserTabs()
+        {
+            using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
+            var debuggerHost = GetDebuggerHost();
+            var jsonResponse = await httpClient.GetStringAsync($"{debuggerHost}/json");
+            return JsonSerializer.Deserialize<BrowserTab[]>(jsonResponse, JsonOptions);
         }
 
         class BrowserTab
         {
+            public string Id { get; set; }
             public string Type { get; set; }
             public string Url { get; set; }
             public string Title { get; set; }

+ 1 - 1
src/Components/Blazor/Templates/src/content/BlazorWasm-CSharp/Client/BlazorWasm-CSharp.Client.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <RazorLangVersion>3.0</RazorLangVersion>
   </PropertyGroup>
 

+ 1 - 1
src/Components/Blazor/Templates/src/content/BlazorWasm-CSharp/Shared/BlazorWasm-CSharp.Shared.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <LangVersion>7.3</LangVersion>
   </PropertyGroup>
 

+ 1 - 1
src/Components/Blazor/Validation/src/Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <Description>Provides experimental support for validation using DataAnnotations.</Description>
     <IsShippingPackage>true</IsShippingPackage>
     <HasReferenceAssembly>false</HasReferenceAssembly>

+ 1 - 14
src/Components/Blazor/testassets/HostedInAspNet.Client/HostedInAspNet.Client.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <OutputType>Exe</OutputType>
     <ReferenceBlazorBuildLocally>true</ReferenceBlazorBuildLocally>
     <RazorLangVersion>3.0</RazorLangVersion>
@@ -11,17 +11,4 @@
     <Reference Include="Microsoft.AspNetCore.Blazor" />
   </ItemGroup>
 
-  <PropertyGroup>
-    <GetCurrentProjectStaticWebAssetsDependsOn>
-      $(GetCurrentProjectStaticWebAssetsDependsOn);
-      _ClearCurrentStaticWebAssetsForReferenceDiscovery
-    </GetCurrentProjectStaticWebAssetsDependsOn>
-  </PropertyGroup>
-
-  <Target Name="_ClearCurrentStaticWebAssetsForReferenceDiscovery">
-    <ItemGroup>
-      <StaticWebAsset Remove="@(StaticWebAsset)" Condition="'%(SourceType)' == ''" />
-    </ItemGroup>
-  </Target>
-
 </Project>

+ 1 - 1
src/Components/Blazor/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj

@@ -8,7 +8,7 @@
     <ProjectReference Include="..\HostedInAspNet.Client\HostedInAspNet.Client.csproj" />
   </ItemGroup>
 
-  <ItemGroup>
+   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Blazor.Server" />
     <Reference Include="Microsoft.AspNetCore" />
     <Reference Include="Microsoft.Extensions.Hosting" />

+ 1 - 1
src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Microsoft.AspNetCore.Blazor.E2EPerformance.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <ReferenceBlazorBuildLocally>true</ReferenceBlazorBuildLocally>
     <RazorLangVersion>3.0</RazorLangVersion>
   </PropertyGroup>

+ 1 - 0
src/Components/Blazor/testassets/MonoSanity/MonoSanity.csproj

@@ -2,6 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+    <!-- This is so that we add the FrameworkReference to Microsoft.AspNetCore.App -->
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 1
src/Components/Blazor/testassets/MonoSanityClient/MonoSanityClient.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Razor" TreatAsLocalProperty="BlazorLinkOnBuild">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <IsPackable>false</IsPackable>
     <BlazorLinkOnBuild>false</BlazorLinkOnBuild>
     <OutputType>exe</OutputType>

+ 1 - 1
src/Components/Blazor/testassets/StandaloneApp/StandaloneApp.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <ReferenceBlazorBuildLocally>true</ReferenceBlazorBuildLocally>
     <RazorLangVersion>3.0</RazorLangVersion>
   </PropertyGroup>

+ 18 - 0
src/Components/Components.sln

@@ -246,6 +246,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests", "Blazor\Validation\test\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests.csproj", "{A5617A9D-C71E-44DE-936C-27611EB40A02}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mono.WebAssembly.Interop", "Mono.WebAssembly.Interop", "{21BB9C13-20C1-4F2B-80E4-D7C64AA3BD05}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.WebAssembly.Interop", "Blazor\Mono.WebAssembly.Interop\src\Mono.WebAssembly.Interop.csproj", "{D141CFEE-D10A-406B-8963-F86FA13732E3}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -1516,6 +1520,18 @@ Global
 		{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x64.Build.0 = Release|Any CPU
 		{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x86.ActiveCfg = Release|Any CPU
 		{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x86.Build.0 = Release|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|x64.Build.0 = Debug|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Debug|x86.Build.0 = Debug|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|x64.ActiveCfg = Release|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|x64.Build.0 = Release|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|x86.ActiveCfg = Release|Any CPU
+		{D141CFEE-D10A-406B-8963-F86FA13732E3}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -1629,6 +1645,8 @@ Global
 		{FD9BD646-9D50-42ED-A3E1-90558BA0C6B2} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
 		{B70F90C7-2696-4050-B24E-BF0308F4E059} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
 		{A5617A9D-C71E-44DE-936C-27611EB40A02} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
+		{21BB9C13-20C1-4F2B-80E4-D7C64AA3BD05} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
+		{D141CFEE-D10A-406B-8963-F86FA13732E3} = {21BB9C13-20C1-4F2B-80E4-D7C64AA3BD05}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {CC3C47E1-AD1A-4619-9CD3-E08A0148E5CE}

+ 1 - 0
src/Components/ComponentsNoDeps.slnf

@@ -17,6 +17,7 @@
       "Blazor\\Templates\\src\\Microsoft.AspNetCore.Blazor.Templates.csproj",
       "Blazor\\Validation\\src\\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj",
       "Blazor\\Validation\\test\\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests.csproj",
+      "Blazor\\Mono.WebAssembly.Interop\\src\\Mono.WebAssembly.Interop.csproj",
       "Blazor\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj",
       "Blazor\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj",
       "Blazor\\testassets\\Microsoft.AspNetCore.Blazor.E2EPerformance\\Microsoft.AspNetCore.Blazor.E2EPerformance.csproj",

+ 4 - 0
src/Components/Directory.Build.props

@@ -12,6 +12,10 @@
   <PropertyGroup>
     <PackageTags>aspnetcore;components</PackageTags>
 
+    <!-- This property points to the latest released Microsoft.AspNetCore.App version it needs to be updated to
+         target the latest patch before a preview release. -->
+    <LatestAspNetCoreReferenceVersion>3.1.0</LatestAspNetCoreReferenceVersion>
+
     <ComponentsSharedSourceRoot>$(MSBuildThisFileDirectory)Shared\</ComponentsSharedSourceRoot>
 
     <!-- So we can use the tool from source within the repo without having to pack -->

+ 20 - 0
src/Components/Directory.Build.targets

@@ -3,6 +3,26 @@
     <GenerateDocumentationFile Condition="'$(GenerateDocumentationFile)' == ''">true</GenerateDocumentationFile>
   </PropertyGroup>
 
+  <!-- We need to do this because our build config interferes with the FrameworkReference definition.
+       This is a way to add the framework defition to the projects that need it (like Blazor Server and
+       Blazor Dev Server) -->
+  <Target Name="_AddAspNetCoreFrameworkReference" BeforeTargets="ProcessFrameworkReferences" Condition="'$(UseLatestAspNetCoreReference)' == 'true' ">
+    <ItemGroup>
+      <FrameworkReference Include="Microsoft.AspNetCore.App" Version="$(LatestAspNetCoreReferenceVersion)" />
+      <KnownFrameworkReference Include="Microsoft.AspNetCore.App">
+        <TargetFramework>netcoreapp3.1</TargetFramework>
+        <RuntimeFrameworkName>Microsoft.AspNetCore.App</RuntimeFrameworkName>
+        <DefaultRuntimeFrameworkVersion>$(LatestAspNetCoreReferenceVersion)</DefaultRuntimeFrameworkVersion>
+        <LatestRuntimeFrameworkVersion>$(LatestAspNetCoreReferenceVersion)</LatestRuntimeFrameworkVersion>
+        <TargetingPackName>Microsoft.AspNetCore.App.Ref</TargetingPackName>
+        <TargetingPackVersion>$(LatestAspNetCoreReferenceVersion)</TargetingPackVersion>
+        <RuntimePackNamePatterns>Microsoft.AspNetCore.App.Runtime.**RID**</RuntimePackNamePatterns>
+        <RuntimePackRuntimeIdentifiers>linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm;win-arm64;win-x64;win-x86</RuntimePackRuntimeIdentifiers>
+        <IsTrimmable>true</IsTrimmable>
+      </KnownFrameworkReference>
+    </ItemGroup>
+  </Target>
+
   <ItemGroup>
     <None Include="$(MSBuildThisFileDirectory)THIRD-PARTY-NOTICES.txt" Pack="true" PackagePath="." />
 

+ 2 - 0
src/Components/Web.JS/Microsoft.AspNetCore.Components.Web.JS.npmproj

@@ -26,6 +26,8 @@
       Private="false" />
   </ItemGroup>
 
+  <Target Name="GetTargetPath" />
+
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Directory.Build.targets))\Directory.Build.targets" />
 
 </Project>

ファイルの差分が大きいため隠しています
+ 0 - 0
src/Components/Web.JS/dist/Release/blazor.server.js


ファイルの差分が大きいため隠しています
+ 0 - 0
src/Components/Web.JS/dist/Release/blazor.webassembly.js


+ 4 - 37
src/Components/Web.JS/src/Boot.WebAssembly.ts

@@ -2,7 +2,6 @@ import '@dotnet/jsinterop';
 import './GlobalExports';
 import * as Environment from './Environment';
 import { monoPlatform } from './Platform/Mono/MonoPlatform';
-import { getAssemblyNameFromUrl } from './Platform/Url';
 import { renderBatch } from './Rendering/Renderer';
 import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
 import { Pointer } from './Platform/Platform';
@@ -39,15 +38,13 @@ async function boot(options?: any): Promise<void> {
 
   // Fetch the boot JSON file
   const bootConfig = await fetchBootConfigAsync();
-  const embeddedResourcesPromise = loadEmbeddedResourcesAsync(bootConfig);
 
   if (!bootConfig.linkerEnabled) {
     console.info('Blazor is running in dev mode without IL stripping. To make the bundle size significantly smaller, publish the application or see https://go.microsoft.com/fwlink/?linkid=870414');
   }
 
   // Determine the URLs of the assemblies we want to load, then begin fetching them all
-  const loadAssemblyUrls = [bootConfig.main]
-    .concat(bootConfig.assemblyReferences)
+  const loadAssemblyUrls = bootConfig.assemblies
     .map(filename => `_framework/_bin/${filename}`);
 
   try {
@@ -56,12 +53,8 @@ async function boot(options?: any): Promise<void> {
     throw new Error(`Failed to start platform. Reason: ${ex}`);
   }
 
-  // Before we start running .NET code, be sure embedded content resources are all loaded
-  await embeddedResourcesPromise;
-
   // Start up the application
-  const mainAssemblyName = getAssemblyNameFromUrl(bootConfig.main);
-  platform.callEntryPoint(mainAssemblyName, bootConfig.entryPoint, []);
+  platform.callEntryPoint(bootConfig.entryAssembly);
 }
 
 async function fetchBootConfigAsync() {
@@ -71,36 +64,10 @@ async function fetchBootConfigAsync() {
   return bootConfigResponse.json() as Promise<BootJsonData>;
 }
 
-function loadEmbeddedResourcesAsync(bootConfig: BootJsonData): Promise<any> {
-  const cssLoadingPromises = bootConfig.cssReferences.map(cssReference => {
-    const linkElement = document.createElement('link');
-    linkElement.rel = 'stylesheet';
-    linkElement.href = cssReference;
-    return loadResourceFromElement(linkElement);
-  });
-  const jsLoadingPromises = bootConfig.jsReferences.map(jsReference => {
-    const scriptElement = document.createElement('script');
-    scriptElement.src = jsReference;
-    return loadResourceFromElement(scriptElement);
-  });
-  return Promise.all(cssLoadingPromises.concat(jsLoadingPromises));
-}
-
-function loadResourceFromElement(element: HTMLElement) {
-  return new Promise((resolve, reject) => {
-    element.onload = resolve;
-    element.onerror = reject;
-    document.head!.appendChild(element);
-  });
-}
-
 // Keep in sync with BootJsonData in Microsoft.AspNetCore.Blazor.Build
 interface BootJsonData {
-  main: string;
-  entryPoint: string;
-  assemblyReferences: string[];
-  cssReferences: string[];
-  jsReferences: string[];
+  entryAssembly: string;
+  assemblies: string[];
   linkerEnabled: boolean;
 }
 

+ 9 - 15
src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts

@@ -11,6 +11,8 @@ let assembly_load: (assemblyName: string) => number;
 let find_class: (assemblyHandle: number, namespace: string, className: string) => number;
 let find_method: (typeHandle: number, methodName: string, unknownArg: number) => MethodHandle;
 let invoke_method: (method: MethodHandle, target: System_Object, argsArrayPtr: number, exceptionFlagIntPtr: number) => System_Object;
+let mono_call_assembly_entry_point: (assemblyName: string, args: System_Object[]) => System_Object;
+let mono_obj_array_new: (length: number) => System_Object;
 let mono_string_get_utf8: (managedString: System_String) => Mono.Utf8Ptr;
 let mono_string: (jsString: string) => System_String;
 const appBinDirName = 'appBinDir';
@@ -39,21 +41,9 @@ export const monoPlatform: Platform = {
 
   findMethod: findMethod,
 
-  callEntryPoint: function callEntryPoint(assemblyName: string, entrypointMethod: string, args: System_Object[]): void {
-    // Parse the entrypointMethod, which is of the form MyApp.MyNamespace.MyTypeName::MyMethodName
-    // Note that we don't support specifying a method overload, so it has to be unique
-    const entrypointSegments = entrypointMethod.split('::');
-    if (entrypointSegments.length != 2) {
-      throw new Error('Malformed entry point method name; could not resolve class name and method name.');
-    }
-    const typeFullName = entrypointSegments[0];
-    const methodName = entrypointSegments[1];
-    const lastDot = typeFullName.lastIndexOf('.');
-    const namespace = lastDot > -1 ? typeFullName.substring(0, lastDot) : '';
-    const typeShortName = lastDot > -1 ? typeFullName.substring(lastDot + 1) : typeFullName;
-
-    const entryPointMethodHandle = monoPlatform.findMethod(assemblyName, namespace, typeShortName, methodName);
-    monoPlatform.callMethod(entryPointMethodHandle, null, args);
+  callEntryPoint: function callEntryPoint(assemblyName: string): System_Object {
+    const empty_array = mono_obj_array_new(0);
+    return mono_call_assembly_entry_point(assemblyName, [empty_array]);
   },
 
   callMethod: function callMethod(method: MethodHandle, target: System_Object, args: System_Object[]): System_Object {
@@ -272,8 +262,12 @@ function createEmscriptenModuleInstance(loadAssemblyUrls: string[], onReady: ()
       'number',
       'number',
     ]);
+
+    mono_call_assembly_entry_point = Module.mono_call_assembly_entry_point;
+
     mono_string_get_utf8 = Module.cwrap('mono_wasm_string_get_utf8', 'number', ['number']);
     mono_string = Module.cwrap('mono_wasm_string_from_js', 'number', ['string']);
+    mono_obj_array_new = Module.cwrap ('mono_wasm_obj_array_new', 'number', ['number']);
 
     MONO.loaded_files = [];
 

+ 2 - 0
src/Components/Web.JS/src/Platform/Mono/MonoTypes.d.ts

@@ -9,6 +9,8 @@ declare namespace Module {
   // These should probably be in @types/emscripten
   function FS_createPath(parent, path, canRead, canWrite);
   function FS_createDataFile(parent, name, data, canRead, canWrite, canOwn);
+
+  function mono_call_assembly_entry_point(assemblyName: string, args: any[]): any;
 }
 
 // Emscripten declares these globals

+ 1 - 1
src/Components/Web.JS/src/Platform/Platform.ts

@@ -1,7 +1,7 @@
 export interface Platform {
   start(loadAssemblyUrls: string[]): Promise<void>;
 
-  callEntryPoint(assemblyName: string, entrypointMethod: string, args: (System_Object | null)[]);
+  callEntryPoint(assemblyName: string): System_Object;
   findMethod(assemblyName: string, namespace: string, className: string, methodName: string): MethodHandle;
   callMethod(method: MethodHandle, target: System_Object | null, args: (System_Object | null)[]): System_Object;
 

+ 1 - 1
src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netstandard2.1</TargetFramework>
     <RazorLangVersion>3.0</RazorLangVersion>
 
     <ReferenceBlazorBuildLocally>true</ReferenceBlazorBuildLocally>

+ 1 - 1
src/Components/test/testassets/TestServer/Components.TestServer.csproj

@@ -4,7 +4,7 @@
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
   </PropertyGroup>
 
-  <ItemGroup>
+   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore" />
     <Reference Include="Microsoft.AspNetCore.Authentication.Cookies" />
     <Reference Include="Microsoft.AspNetCore.Blazor.Server" />

+ 10 - 0
src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.cpp

@@ -47,6 +47,7 @@ void HostFxr::Load(HMODULE moduleHandle)
         m_corehost_set_error_writer_fn = ModuleHelpers::GetKnownProcAddress<corehost_set_error_writer_fn>(moduleHandle, "hostfxr_set_error_writer", /* optional */ true);
         m_hostfxr_initialize_for_dotnet_commandline_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_initialize_for_dotnet_runtime_fn>(moduleHandle, "hostfxr_initialize_for_dotnet_command_line", /* optional */ true);
         m_hostfxr_set_runtime_property_value_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_set_runtime_property_value_fn>(moduleHandle, "hostfxr_set_runtime_property_value", /* optional */ true);
+        m_hostfxr_get_runtime_property_value_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_get_runtime_property_value_fn>(moduleHandle, "hostfxr_get_runtime_property_value", /* optional */ true);
         m_hostfxr_run_app_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_run_app_fn>(moduleHandle, "hostfxr_run_app", /* optional */ true);
         m_hostfxr_close_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_close_fn>(moduleHandle, "hostfxr_close", /* optional */ true);
     }
@@ -164,6 +165,15 @@ int HostFxr::SetRuntimePropertyValue(PCWSTR name, PCWSTR value) const noexcept
     return 0;
 }
 
+int HostFxr::GetRuntimePropertyValue(PCWSTR name, PWSTR* value) const noexcept
+{
+    if (m_host_context_handle != nullptr && m_hostfxr_get_runtime_property_value_fn != nullptr)
+    {
+        return m_hostfxr_get_runtime_property_value_fn(m_host_context_handle, name, value);
+    }
+    return 0;
+}
+
 void HostFxr::Close() const noexcept
 {
     if (m_host_context_handle != nullptr && m_hostfxr_close_fn != nullptr)

+ 3 - 0
src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.h

@@ -25,6 +25,7 @@ typedef void(*corehost_error_writer_fn) (const WCHAR* message);
 typedef corehost_error_writer_fn(*corehost_set_error_writer_fn) (corehost_error_writer_fn error_writer);
 typedef int(*hostfxr_initialize_for_dotnet_runtime_fn)(int argc, const PCWSTR* argv, hostfxr_initialize_parameters* parameters, void* const* host_context_handle);
 typedef int(*hostfxr_set_runtime_property_value_fn)(void* host_context_handle, PCWSTR name, PCWSTR value);
+typedef int(*hostfxr_get_runtime_property_value_fn)(void* host_context_handle, PCWSTR name, PWSTR* value);
 typedef int(*hostfxr_run_app_fn)(void* host_context_handle);
 typedef int(*hostfxr_close_fn)(void* hostfxr_context_handle);
 
@@ -73,6 +74,7 @@ public:
 
     HostFxrErrorRedirector RedirectOutput(RedirectionOutput* writer) const noexcept;
     int SetRuntimePropertyValue(PCWSTR name, PCWSTR value) const noexcept;
+    int GetRuntimePropertyValue(PCWSTR name, PWSTR* value) const noexcept;
     int InitializeForApp(int argc, PCWSTR* argv, const std::wstring& m_dotnetExeKnownLocation) const noexcept;
     void Close() const noexcept;
 
@@ -82,6 +84,7 @@ private:
     hostfxr_get_native_search_directories_fn m_hostfxr_get_native_search_directories_fn;
     hostfxr_initialize_for_dotnet_runtime_fn m_hostfxr_initialize_for_dotnet_commandline_fn;
     hostfxr_set_runtime_property_value_fn m_hostfxr_set_runtime_property_value_fn;
+    hostfxr_get_runtime_property_value_fn m_hostfxr_get_runtime_property_value_fn;
     hostfxr_run_app_fn m_hostfxr_run_app_fn;
     corehost_set_error_writer_fn m_corehost_set_error_writer_fn;
     hostfxr_close_fn m_hostfxr_close_fn;

+ 8 - 0
src/Servers/IIS/AspNetCoreModuleV2/CommonLib/config_utility.h

@@ -14,6 +14,7 @@ class ConfigUtility
     #define CS_ASPNETCORE_HANDLER_SETTINGS                   L"handlerSettings"
     #define CS_ASPNETCORE_HANDLER_VERSION                    L"handlerVersion"
     #define CS_ASPNETCORE_DEBUG_FILE                         L"debugFile"
+    #define CS_ASPNETCORE_ENABLE_OUT_OF_PROCESS_CONSOLE_REDIRECTION L"enableOutOfProcessConsoleRedirection"
     #define CS_ASPNETCORE_FORWARD_RESPONSE_CONNECTION_HEADER L"forwardResponseConnectionHeader"
     #define CS_ASPNETCORE_DEBUG_LEVEL                        L"debugLevel"
     #define CS_ASPNETCORE_HANDLER_SETTINGS_NAME              L"name"
@@ -41,6 +42,13 @@ public:
         return FindKeyValuePair(pElement, CS_ASPNETCORE_DEBUG_LEVEL, strDebugFile);
     }
 
+    static
+    HRESULT
+    FindEnableOutOfProcessConsoleRedirection(IAppHostElement* pElement, STRU& strEnableOutOfProcessConsoleRedirection)
+    {
+        return FindKeyValuePair(pElement, CS_ASPNETCORE_ENABLE_OUT_OF_PROCESS_CONSOLE_REDIRECTION, strEnableOutOfProcessConsoleRedirection);
+    }
+
     static
     HRESULT
     FindForwardResponseConnectionHeader(IAppHostElement* pElement, STRU& strForwardResponseConnectionHeader)

+ 14 - 1
src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp

@@ -252,7 +252,20 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
 
         if (m_pConfig->QueryCallStartupHook())
         {
-            RETURN_IF_NOT_ZERO(context->m_hostFxr.SetRuntimePropertyValue(DOTNETCORE_STARTUP_HOOK, ASPNETCORE_STARTUP_ASSEMBLY));
+            PWSTR startupHookValue = NULL;
+            // Will get property not found if the enviroment variable isn't set.
+            context->m_hostFxr.GetRuntimePropertyValue(DOTNETCORE_STARTUP_HOOK, &startupHookValue);
+            
+            if (startupHookValue == NULL)
+            {
+                RETURN_IF_NOT_ZERO(context->m_hostFxr.SetRuntimePropertyValue(DOTNETCORE_STARTUP_HOOK, ASPNETCORE_STARTUP_ASSEMBLY));
+            }
+            else
+            {
+                std::wstring startupHook(startupHookValue);
+                startupHook.append(L";").append(ASPNETCORE_STARTUP_ASSEMBLY);
+                RETURN_IF_NOT_ZERO(context->m_hostFxr.SetRuntimePropertyValue(DOTNETCORE_STARTUP_HOOK, startupHook.c_str()));
+            }
         }
 
         RETURN_IF_NOT_ZERO(context->m_hostFxr.SetRuntimePropertyValue(DOTNETCORE_USE_ENTRYPOINT_FILTER, L"1"));

+ 1 - 0
src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp

@@ -158,6 +158,7 @@ PROCESS_MANAGER::GetProcess(
                     pConfig->QueryAnonymousAuthEnabled(),
                     pConfig->QueryEnvironmentVariables(),
                     pConfig->QueryStdoutLogEnabled(),
+                    pConfig->QueryEnableOutOfProcessConsoleRedirection(),
                     fWebsocketSupported,
                     pConfig->QueryStdoutLogFile(),
                     pConfig->QueryApplicationPhysicalPath(),   // physical path

+ 28 - 4
src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp

@@ -25,6 +25,7 @@ SERVER_PROCESS::Initialize(
     BOOL                  fAnonymousAuthEnabled,
     std::map<std::wstring, std::wstring, ignore_case_comparer>& pEnvironmentVariables,
     BOOL                  fStdoutLogEnabled,
+    BOOL                  fEnableOutOfProcessConsoleRedirection,
     BOOL                  fWebSocketSupported,
     STRU                  *pstruStdoutLogFile,
     STRU                  *pszAppPhysicalPath,
@@ -43,6 +44,7 @@ SERVER_PROCESS::Initialize(
     m_fWindowsAuthEnabled = fWindowsAuthEnabled;
     m_fBasicAuthEnabled = fBasicAuthEnabled;
     m_fAnonymousAuthEnabled = fAnonymousAuthEnabled;
+    m_fEnableOutOfProcessConsoleRedirection = fEnableOutOfProcessConsoleRedirection;
     m_pProcessManager->ReferenceProcessManager();
     m_fDebuggerAttached = FALSE;
 
@@ -1030,6 +1032,15 @@ SERVER_PROCESS::SetupStdHandles(
     saAttr.bInheritHandle = TRUE;
     saAttr.lpSecurityDescriptor = NULL;
 
+    if (!m_fEnableOutOfProcessConsoleRedirection)
+    {
+        pStartupInfo->dwFlags = STARTF_USESTDHANDLES;
+        pStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
+        pStartupInfo->hStdError = INVALID_HANDLE_VALUE;
+        pStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
+        return hr;
+    }
+
     if (!m_fStdoutLogEnabled)
     {
         CreatePipe(&m_hStdoutHandle, &m_hStdErrWritePipe, &saAttr, 0 /*nSize*/);
@@ -1770,6 +1781,8 @@ SERVER_PROCESS::SERVER_PROCESS() :
     m_dwListeningProcessId(0),
     m_hListeningProcessHandle(NULL),
     m_hShutdownHandle(NULL),
+    m_hStdErrWritePipe(NULL),
+    m_hReadThread(nullptr),
     m_randomGenerator(std::random_device()())
 {
     //InterlockedIncrement(&g_dwActiveServerProcesses);
@@ -1866,13 +1879,15 @@ SERVER_PROCESS::~SERVER_PROCESS()
         m_pProcessManager = NULL;
     }
 
-    if (m_hStdoutHandle != NULL)
+    if (m_hStdErrWritePipe != NULL)
     {
-        if (m_hStdoutHandle != INVALID_HANDLE_VALUE)
+        if (m_hStdErrWritePipe != INVALID_HANDLE_VALUE)
         {
-            CloseHandle(m_hStdoutHandle);
+            FlushFileBuffers(m_hStdErrWritePipe);
+            CloseHandle(m_hStdErrWritePipe);
         }
-        m_hStdoutHandle = NULL;
+
+        m_hStdErrWritePipe = NULL;
     }
 
     // Forces ReadFile to cancel, causing the read loop to complete.
@@ -1907,6 +1922,15 @@ SERVER_PROCESS::~SERVER_PROCESS()
         m_hReadThread = nullptr;
     }
 
+    if (m_hStdoutHandle != NULL)
+    {
+        if (m_hStdoutHandle != INVALID_HANDLE_VALUE)
+        {
+            CloseHandle(m_hStdoutHandle);
+        }
+        m_hStdoutHandle = NULL;
+    }
+
     if (m_fStdoutLogEnabled)
     {
         m_Timer.CancelTimer();

+ 2 - 0
src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.h

@@ -37,6 +37,7 @@ public:
         _In_ BOOL                  fAnonymousAuthEnabled,
         _In_ std::map<std::wstring, std::wstring, ignore_case_comparer>& pEnvironmentVariables,
         _In_ BOOL                  fStdoutLogEnabled,
+        _In_ BOOL                  fDisableRedirection,
         _In_ BOOL                  fWebSocketSupported,
         _In_ STRU                 *pstruStdoutLogFile,
         _In_ STRU                 *pszAppPhysicalPath,
@@ -253,6 +254,7 @@ private:
     BOOL                    m_fBasicAuthEnabled;
     BOOL                    m_fAnonymousAuthEnabled;
     BOOL                    m_fDebuggerAttached;
+    BOOL                    m_fEnableOutOfProcessConsoleRedirection;
 
     STTIMER                 m_Timer;
     SOCKET                  m_socket;

+ 6 - 0
src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp

@@ -379,6 +379,12 @@ REQUESTHANDLER_CONFIG::Populate(
         goto Finished;
     }
 
+    hr = ConfigUtility::FindEnableOutOfProcessConsoleRedirection(pAspNetCoreElement, m_fEnableOutOfProcessConsoleRedirection);
+    if (FAILED(hr))
+    {
+        goto Finished;
+    }
+
     hr = ConfigUtility::FindForwardResponseConnectionHeader(pAspNetCoreElement, m_struForwardResponseConnectionHeader);
     if (FAILED(hr))
     {

+ 7 - 0
src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.h

@@ -218,6 +218,12 @@ public:
         return &m_struConfigPath;
     }
 
+    BOOL
+    QueryEnableOutOfProcessConsoleRedirection()
+    {
+        return !m_fEnableOutOfProcessConsoleRedirection.Equals(L"false", 1);
+    }
+
     STRU*
     QueryForwardResponseConnectionHeader()
     {
@@ -262,6 +268,7 @@ protected:
     BOOL                   m_fWindowsAuthEnabled;
     BOOL                   m_fBasicAuthEnabled;
     BOOL                   m_fAnonymousAuthEnabled;
+    STRU                   m_fEnableOutOfProcessConsoleRedirection;
     APP_HOSTING_MODEL      m_hostingModel;
     std::map<std::wstring, std::wstring, ignore_case_comparer> m_pEnvironmentVariables;
     STRU                   m_struHostFxrLocation;

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません