From e822f5f12dcdbe14de7c49f03256f8ea92f84efe Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 20 Jul 2020 13:25:49 -0700 Subject: [PATCH] Create a Blazor WebAssembly SDK (#24044) --- AspNetCore.sln | 63 ++ eng/Build.props | 1 + eng/ProjectReferences.props | 1 + src/Components/Components.slnf | 3 + src/Components/ComponentsNoDeps.slnf | 4 + src/Components/Directory.Build.props | 8 - src/Components/Directory.Build.targets | 2 +- ....BlazorWebAssembly.IntegrationTests.csproj | 78 +++ .../integrationtests}/ServiceWorkerAssert.cs | 2 +- .../WasmBuildIncrementalismTest.cs | 2 +- .../WasmBuildIntegrationTest.cs | 2 +- .../WasmBuildLazyLoadTest.cs | 2 +- .../integrationtests}/WasmCompressionTests.cs | 0 .../WasmPublishIntegrationTest.cs | 18 +- .../integrationtests}/WasmPwaManifestTests.cs | 0 .../Sdk/integrationtests/xunit.runner.json | 5 + .../WebAssembly/Sdk/src/AssetsManifestFile.cs | 33 + .../src/BlazorReadSatelliteAssemblyFile.cs | 38 ++ .../src/BlazorWriteSatelliteAssemblyFile.cs | 53 ++ .../WebAssembly/Sdk/src/BootJsonData.cs | 85 +++ .../WebAssembly/Sdk/src/BrotliCompress.cs | 125 ++++ .../CreateBlazorTrimmerRootDescriptorFile.cs | 79 +++ .../WebAssembly/Sdk/src/GZipCompress.cs | 70 +++ .../src/GenerateBlazorWebAssemblyBootJson.cs | 155 +++++ .../GenerateServiceWorkerAssetsManifest.cs | 97 +++ ...Microsoft.NET.Sdk.BlazorWebAssembly.csproj | 89 +++ ...Microsoft.NET.Sdk.BlazorWebAssembly.nuspec | 19 + .../WebAssembly/Sdk/src/Sdk/Sdk.props | 22 + .../WebAssembly/Sdk/src/Sdk/Sdk.targets | 20 + .../WebAssembly/Sdk/src/_._} | 0 .../Microsoft.NET.Sdk.BlazorWebAssembly.props | 16 + ...icrosoft.NET.Sdk.BlazorWebAssembly.targets | 16 + .../Sdk/src/targets/BlazorWasm.web.config | 40 ++ .../Sdk/src/targets/LinkerWorkaround.xml | 15 + ...ft.NET.Sdk.BlazorWebAssembly.Current.props | 36 ++ ....NET.Sdk.BlazorWebAssembly.Current.targets | 582 ++++++++++++++++++ ...sembly.ServiceWorkerAssetsManifest.targets | 169 +++++ .../BlazorReadSatelliteAssemblyFileTest.cs | 68 ++ .../Sdk/test/GenerateBlazorBootJsonTest.cs | 195 ++++++ ...oft.NET.Sdk.BlazorWebAssembly.Tests.csproj | 12 + .../Sdk/testassets/Directory.Build.props | 50 ++ .../Sdk/testassets/Directory.Build.targets | 7 + .../LinkBaseToWebRoot/js/LinkedScript.js | 0 .../RestoreBlazorWasmTestProjects.csproj | 12 + .../Sdk/testassets/blazor.webassembly.js | 1 + .../testassets/blazorhosted-rid/Program.cs | 0 .../blazorhosted-rid/blazorhosted-rid.csproj | 0 .../Sdk}/testassets/blazorhosted/Program.cs | 0 .../blazorhosted/blazorhosted.csproj | 0 .../Sdk}/testassets/blazorwasm/App.razor | 0 .../blazorwasm/LinkToWebRoot/css/app.css | 0 .../testassets/blazorwasm/Pages/Index.razor | 0 .../Sdk}/testassets/blazorwasm/Program.cs | 0 .../blazorwasm/Resources.ja.resx.txt | 0 .../Sdk}/testassets/blazorwasm/_Imports.razor | 0 .../testassets/blazorwasm/blazorwasm.csproj | 8 +- .../blazorwasm/wwwroot/Fake-License.txt | 0 .../testassets/blazorwasm/wwwroot/css/app.css | 0 .../testassets/blazorwasm/wwwroot/index.html | 0 .../serviceworkers/my-prod-service-worker.js | 0 .../serviceworkers/my-service-worker.js | 0 .../Class1.cs | 0 .../Resources.es-ES.resx | 0 ...classlibrarywithsatelliteassemblies.csproj | 0 .../testassets/razorclasslibrary/Class1.cs | 0 .../RazorClassLibrary.csproj | 0 .../razorclasslibrary/wwwroot/styles.css | 0 .../wwwroot/wwwroot/exampleJsInterop.js | 0 .../WebAssembly/Sdk/tools/Application.cs | 98 +++ .../Sdk/tools/BrotliCompressCommand.cs | 85 +++ .../WebAssembly/Sdk/tools/DebugMode.cs | 27 + ...oft.NET.Sdk.BlazorWebAssembly.Tools.csproj | 22 + .../WebAssembly/Sdk/tools/Program.cs | 30 + .../Sdk/tools/runtimeconfig.template.json | 3 + ...soft.NET.Sdk.Razor.IntegrationTests.csproj | 48 +- src/Shared/E2ETesting/E2ETesting.targets | 9 +- .../MSBuild.Testing}/Assert.cs | 0 .../MSBuild.Testing}/BuildVariables.cs | 6 +- .../MSBuild.Testing}/FIleThumbPrint.cs | 0 .../MSBuild.Testing/MSBuild.Testing.targets | 53 ++ .../MSBuild.Testing}/MSBuildProcessKind.cs | 0 .../MSBuild.Testing}/MSBuildProcessManager.cs | 3 +- .../MSBuild.Testing}/MSBuildResult.cs | 0 .../MSBuild.Testing}/ProjectDirectory.cs | 10 +- 84 files changed, 2625 insertions(+), 72 deletions(-) create mode 100644 src/Components/WebAssembly/Sdk/integrationtests/Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/ServiceWorkerAssert.cs (98%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmBuildIncrementalismTest.cs (99%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmBuildIntegrationTest.cs (99%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmBuildLazyLoadTest.cs (99%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmCompressionTests.cs (100%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmPublishIntegrationTest.cs (98%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmPwaManifestTests.cs (100%) create mode 100644 src/Components/WebAssembly/Sdk/integrationtests/xunit.runner.json create mode 100644 src/Components/WebAssembly/Sdk/src/AssetsManifestFile.cs create mode 100644 src/Components/WebAssembly/Sdk/src/BlazorReadSatelliteAssemblyFile.cs create mode 100644 src/Components/WebAssembly/Sdk/src/BlazorWriteSatelliteAssemblyFile.cs create mode 100644 src/Components/WebAssembly/Sdk/src/BootJsonData.cs create mode 100644 src/Components/WebAssembly/Sdk/src/BrotliCompress.cs create mode 100644 src/Components/WebAssembly/Sdk/src/CreateBlazorTrimmerRootDescriptorFile.cs create mode 100644 src/Components/WebAssembly/Sdk/src/GZipCompress.cs create mode 100644 src/Components/WebAssembly/Sdk/src/GenerateBlazorWebAssemblyBootJson.cs create mode 100644 src/Components/WebAssembly/Sdk/src/GenerateServiceWorkerAssetsManifest.cs create mode 100644 src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.csproj create mode 100644 src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.nuspec create mode 100644 src/Components/WebAssembly/Sdk/src/Sdk/Sdk.props create mode 100644 src/Components/WebAssembly/Sdk/src/Sdk/Sdk.targets rename src/{Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js => Components/WebAssembly/Sdk/src/_._} (100%) create mode 100644 src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.props create mode 100644 src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.targets create mode 100644 src/Components/WebAssembly/Sdk/src/targets/BlazorWasm.web.config create mode 100644 src/Components/WebAssembly/Sdk/src/targets/LinkerWorkaround.xml create mode 100644 src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.props create mode 100644 src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets create mode 100644 src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets create mode 100644 src/Components/WebAssembly/Sdk/test/BlazorReadSatelliteAssemblyFileTest.cs create mode 100644 src/Components/WebAssembly/Sdk/test/GenerateBlazorBootJsonTest.cs create mode 100644 src/Components/WebAssembly/Sdk/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj create mode 100644 src/Components/WebAssembly/Sdk/testassets/Directory.Build.props create mode 100644 src/Components/WebAssembly/Sdk/testassets/Directory.Build.targets rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/LinkBaseToWebRoot/js/LinkedScript.js (100%) create mode 100644 src/Components/WebAssembly/Sdk/testassets/RestoreBlazorWasmTestProjects/RestoreBlazorWasmTestProjects.csproj create mode 100644 src/Components/WebAssembly/Sdk/testassets/blazor.webassembly.js rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorhosted-rid/Program.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorhosted-rid/blazorhosted-rid.csproj (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorhosted/Program.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorhosted/blazorhosted.csproj (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/App.razor (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/LinkToWebRoot/css/app.css (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/Pages/Index.razor (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/Program.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/Resources.ja.resx.txt (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/_Imports.razor (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/blazorwasm.csproj (88%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/Fake-License.txt (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/css/app.css (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/index.html (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/classlibrarywithsatelliteassemblies/Class1.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/razorclasslibrary/Class1.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/razorclasslibrary/RazorClassLibrary.csproj (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/razorclasslibrary/wwwroot/styles.css (100%) create mode 100644 src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js create mode 100644 src/Components/WebAssembly/Sdk/tools/Application.cs create mode 100644 src/Components/WebAssembly/Sdk/tools/BrotliCompressCommand.cs create mode 100644 src/Components/WebAssembly/Sdk/tools/DebugMode.cs create mode 100644 src/Components/WebAssembly/Sdk/tools/Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj create mode 100644 src/Components/WebAssembly/Sdk/tools/Program.cs create mode 100644 src/Components/WebAssembly/Sdk/tools/runtimeconfig.template.json rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/Assert.cs (100%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/BuildVariables.cs (83%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/FIleThumbPrint.cs (100%) create mode 100644 src/Shared/MSBuild.Testing/MSBuild.Testing.targets rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/MSBuildProcessKind.cs (100%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/MSBuildProcessManager.cs (98%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/MSBuildResult.cs (100%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/ProjectDirectory.cs (97%) diff --git a/AspNetCore.sln b/AspNetCore.sln index ca9dfcf281..50068277d8 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1427,6 +1427,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Web.Extensions.Tests", "src\Components\Web.Extensions\test\Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj", "{157605CB-5170-4C1A-980F-4BAE42DB60DE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{FED4267E-E5E4-49C5-98DB-8B3F203596EE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly", "src\Components\WebAssembly\Sdk\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", "{6B2734BF-C61D-4889-ABBF-456A4075D59B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tests", "src\Components\WebAssembly\Sdk\test\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj", "{83371889-9A3E-4D16-AE77-EB4F83BC6374}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests", "src\Components\WebAssembly\Sdk\integrationtests\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj", "{525EBCB4-A870-470B-BC90-845306C337D1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tools", "src\Components\WebAssembly\Sdk\tools\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj", "{175E5CD8-92D4-46BB-882E-3A930D3302D4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -6729,6 +6739,54 @@ Global {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x64.Build.0 = Release|Any CPU {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.ActiveCfg = Release|Any CPU {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.Build.0 = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x64.ActiveCfg = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x64.Build.0 = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x86.ActiveCfg = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x86.Build.0 = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|Any CPU.Build.0 = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|x64.ActiveCfg = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|x64.Build.0 = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|x86.ActiveCfg = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|x86.Build.0 = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|x64.ActiveCfg = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|x64.Build.0 = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|x86.ActiveCfg = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|x86.Build.0 = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|Any CPU.Build.0 = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|x64.ActiveCfg = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|x64.Build.0 = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|x86.ActiveCfg = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|x86.Build.0 = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|x64.ActiveCfg = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|x64.Build.0 = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|x86.Build.0 = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|Any CPU.Build.0 = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|x64.ActiveCfg = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|x64.Build.0 = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|x86.ActiveCfg = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|x86.Build.0 = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|x64.ActiveCfg = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|x64.Build.0 = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|x86.ActiveCfg = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|x86.Build.0 = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|Any CPU.Build.0 = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x64.ActiveCfg = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x64.Build.0 = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x86.ActiveCfg = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -7444,6 +7502,11 @@ Global {F71FE795-9923-461B-9809-BB1821A276D0} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF} {8294A74F-7DAA-4B69-BC56-7634D93C9693} = {F71FE795-9923-461B-9809-BB1821A276D0} {157605CB-5170-4C1A-980F-4BAE42DB60DE} = {F71FE795-9923-461B-9809-BB1821A276D0} + {FED4267E-E5E4-49C5-98DB-8B3F203596EE} = {562D5067-8CD8-4F19-BCBB-873204932C61} + {6B2734BF-C61D-4889-ABBF-456A4075D59B} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} + {83371889-9A3E-4D16-AE77-EB4F83BC6374} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} + {525EBCB4-A870-470B-BC90-845306C337D1} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} + {175E5CD8-92D4-46BB-882E-3A930D3302D4} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/eng/Build.props b/eng/Build.props index 56b4917250..3740546dd6 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -172,6 +172,7 @@ @(ProjectToBuild); @(ProjectToExclude); $(RepoRoot)src\Razor\test\testassets\**\*.*proj; + $(RepoRoot)src\Components\WebAssembly\Sdk\testassets\**\*.*proj; $(RepoRoot)**\node_modules\**\*; $(RepoRoot)**\bin\**\*; $(RepoRoot)**\obj\**\*;" diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index a0e89782ca..6dbfca4646 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -145,6 +145,7 @@ + diff --git a/src/Components/Components.slnf b/src/Components/Components.slnf index c202f7c1c6..637ec1a384 100644 --- a/src/Components/Components.slnf +++ b/src/Components/Components.slnf @@ -110,6 +110,9 @@ "src\\Components\\WebAssembly\\WebAssembly.Authentication\\test\\Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj", "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Client\\Wasm.Authentication.Client.csproj", "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Shared\\Wasm.Authentication.Shared.csproj", + "src\\Components\\WebAssembly\\Sdk\\src\\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", + "src\\Components\\WebAssembly\\Sdk\\test\\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj", + "src\\Components\\WebAssembly\\Sdk\\integrationtests\\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj", "src\\JSInterop\\Microsoft.JSInterop\\src\\Microsoft.JSInterop.csproj" ] } diff --git a/src/Components/ComponentsNoDeps.slnf b/src/Components/ComponentsNoDeps.slnf index 6dbf031fd3..dd08970d02 100644 --- a/src/Components/ComponentsNoDeps.slnf +++ b/src/Components/ComponentsNoDeps.slnf @@ -31,6 +31,10 @@ "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj", "src\\Components\\WebAssembly\\testassets\\StandaloneApp\\StandaloneApp.csproj", + "src\\Components\\WebAssembly\\Sdk\\src\\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", + "src\\Components\\WebAssembly\\Sdk\\test\\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj", + "src\\Components\\WebAssembly\\Sdk\\tools\\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj", + "src\\Components\\WebAssembly\\Sdk\\integrationtests\\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj", "src\\Components\\Web\\src\\Microsoft.AspNetCore.Components.Web.csproj", "src\\Components\\Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj", "src\\Components\\benchmarkapps\\Wasm.Performance\\Driver\\Wasm.Performance.Driver.csproj", diff --git a/src/Components/Directory.Build.props b/src/Components/Directory.Build.props index 1704a1b070..b1ea764edd 100644 --- a/src/Components/Directory.Build.props +++ b/src/Components/Directory.Build.props @@ -1,14 +1,6 @@ - - - - - - - - true diff --git a/src/Components/Directory.Build.targets b/src/Components/Directory.Build.targets index 69204d61a6..f3143253af 100644 --- a/src/Components/Directory.Build.targets +++ b/src/Components/Directory.Build.targets @@ -3,7 +3,7 @@ $(RepoRoot)src\Components\Web.JS\dist\$(Configuration)\blazor.webassembly.js $(BlazorWebAssemblyJSPath).map - <_BlazorDevServerPath>$(RepoRoot)src/Components/WebAssembly/DevServer/src/bin/$(Configuration)/$(DefaultNetCoreTargetFramework)/blazor-devserver.dll + <_BlazorDevServerPath>$(ArtifactsDir)/bin/Microsoft.AspNetCore.Components.WebAssembly.DevServer/$(Configuration)/$(DefaultNetCoreTargetFramework)/blazor-devserver.dll dotnet exec "$(_BlazorDevServerPath)" serve --applicationpath "$(TargetPath)" $(AdditionalRunArguments) diff --git a/src/Components/WebAssembly/Sdk/integrationtests/Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj b/src/Components/WebAssembly/Sdk/integrationtests/Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj new file mode 100644 index 0000000000..4255ce7c73 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/integrationtests/Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj @@ -0,0 +1,78 @@ + + + + + $(DefaultNetCoreTargetFramework) + true + + + false + $(MSBuildProjectDirectory)\..\testassets\ + + + + + + + + + + + + + + + + + + false + true + + + + false + true + + + + + + + + + + + + + + + + + <_DesktopMSBuildPath Condition="'$(OS)' == 'Windows_NT' and Exists('$(_VSInstallDir)\MSBuild\Current\Bin\msbuild.exe')">$(_VSInstallDir)\MSBuild\Current\Bin\msbuild.exe + <_DesktopMSBuildPath Condition="'$(OS)' == 'Windows_NT' and Exists('$(_VSInstallDir)\MSBuild\15.0\Bin\msbuild.exe')">$(_VSInstallDir)\MSBuild\15.0\Bin\msbuild.exe + + + + + + + <_Parameter1>DesktopMSBuildPath + <_Parameter2>$(_DesktopMSBuildPath) + + + + + + + + + + + + + diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs b/src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs similarity index 98% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs rename to src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs index 7585b5b76c..8597d10480 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIncrementalismTest.cs similarity index 99% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIncrementalismTest.cs index f91dd03863..218567fcc5 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIncrementalismTest.cs @@ -4,7 +4,7 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; using Xunit; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIntegrationTest.cs similarity index 99% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIntegrationTest.cs index 12fbbd0380..c521c6be7e 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIntegrationTest.cs @@ -4,7 +4,7 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; using Microsoft.AspNetCore.Testing; using Xunit; diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildLazyLoadTest.cs similarity index 99% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmBuildLazyLoadTest.cs index e8f1a02d40..424caeda2d 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildLazyLoadTest.cs @@ -4,7 +4,7 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; using Xunit; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmCompressionTests.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmCompressionTests.cs diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs similarity index 98% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs index 45c878ea5a..1108d0bff9 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs @@ -5,7 +5,7 @@ using System.IO; using System.IO.Compression; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; using Microsoft.AspNetCore.Testing; using Xunit; using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert; @@ -608,6 +608,17 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests assetsManifestPath: "custom-service-worker-assets.js"); } + [Fact] + public async Task Publish_HostedApp_WithRidSpecifiedInCLI_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted-rid", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); + project.RuntimeIdentifier = "linux-x64"; + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/p:RuntimeIdentifier=linux-x64"); + + AssertRIDPublishOuput(project, result); + } + [Fact] public async Task Publish_HostedApp_WithRid_Works() { @@ -616,6 +627,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests project.RuntimeIdentifier = "linux-x64"; var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + AssertRIDPublishOuput(project, result); + } + + private static void AssertRIDPublishOuput(ProjectDirectory project, MSBuildResult result) + { Assert.BuildPassed(result); var publishDirectory = project.PublishOutputDirectory; diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmPwaManifestTests.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmPwaManifestTests.cs diff --git a/src/Components/WebAssembly/Sdk/integrationtests/xunit.runner.json b/src/Components/WebAssembly/Sdk/integrationtests/xunit.runner.json new file mode 100644 index 0000000000..d00e4ae907 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/integrationtests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "methodDisplay": "method", + "shadowCopy": false, + "maxParallelThreads": -1 +} \ No newline at end of file diff --git a/src/Components/WebAssembly/Sdk/src/AssetsManifestFile.cs b/src/Components/WebAssembly/Sdk/src/AssetsManifestFile.cs new file mode 100644 index 0000000000..872b17aae2 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/AssetsManifestFile.cs @@ -0,0 +1,33 @@ +// 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.NET.Sdk.BlazorWebAssembly +{ +#pragma warning disable IDE1006 // Naming Styles + public class AssetsManifestFile + { + /// + /// Gets or sets a version string. + /// + public string version { get; set; } + + /// + /// Gets or sets the assets. Keys are URLs; values are base-64-formatted SHA256 content hashes. + /// + public AssetsManifestFileEntry[] assets { get; set; } + } + + public class AssetsManifestFileEntry + { + /// + /// Gets or sets the asset URL. Normally this will be relative to the application's base href. + /// + public string url { get; set; } + + /// + /// Gets or sets the file content hash. This should be the base-64-formatted SHA256 value. + /// + public string hash { get; set; } + } +#pragma warning restore IDE1006 // Naming Styles +} diff --git a/src/Components/WebAssembly/Sdk/src/BlazorReadSatelliteAssemblyFile.cs b/src/Components/WebAssembly/Sdk/src/BlazorReadSatelliteAssemblyFile.cs new file mode 100644 index 0000000000..f6bf0c88f8 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/BlazorReadSatelliteAssemblyFile.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.Linq; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class BlazorReadSatelliteAssemblyFile : Task + { + [Output] + public ITaskItem[] SatelliteAssembly { get; set; } + + [Required] + public ITaskItem ReadFile { get; set; } + + public override bool Execute() + { + var document = XDocument.Load(ReadFile.ItemSpec); + SatelliteAssembly = document.Root + .Elements() + .Select(e => + { + // + + var taskItem = new TaskItem(e.Attribute("Name").Value); + taskItem.SetMetadata("Culture", e.Attribute("Culture").Value); + taskItem.SetMetadata("DestinationSubDirectory", e.Attribute("DestinationSubDirectory").Value); + + return taskItem; + }).ToArray(); + + return true; + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/BlazorWriteSatelliteAssemblyFile.cs b/src/Components/WebAssembly/Sdk/src/BlazorWriteSatelliteAssemblyFile.cs new file mode 100644 index 0000000000..92fd8d33e8 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/BlazorWriteSatelliteAssemblyFile.cs @@ -0,0 +1,53 @@ +// 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; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class BlazorWriteSatelliteAssemblyFile : Task + { + [Required] + public ITaskItem[] SatelliteAssembly { get; set; } + + [Required] + public ITaskItem WriteFile { get; set; } + + public override bool Execute() + { + using var fileStream = File.Create(WriteFile.ItemSpec); + WriteSatelliteAssemblyFile(fileStream); + return true; + } + + internal void WriteSatelliteAssemblyFile(Stream stream) + { + var root = new XElement("SatelliteAssembly"); + + foreach (var item in SatelliteAssembly) + { + // + + root.Add(new XElement("Assembly", + new XAttribute("Name", item.ItemSpec), + new XAttribute("Culture", item.GetMetadata("Culture")), + new XAttribute("DestinationSubDirectory", item.GetMetadata("DestinationSubDirectory")))); + } + + var xmlWriterSettings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true + }; + + using var writer = XmlWriter.Create(stream, xmlWriterSettings); + var xDocument = new XDocument(root); + + xDocument.Save(writer); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/BootJsonData.cs b/src/Components/WebAssembly/Sdk/src/BootJsonData.cs new file mode 100644 index 0000000000..b3a2dbd388 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/BootJsonData.cs @@ -0,0 +1,85 @@ +// 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.Runtime.Serialization; +using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ +#pragma warning disable IDE1006 // Naming Styles + /// + /// Defines the structure of a Blazor boot JSON file + /// + public class BootJsonData + { + /// + /// Gets the name of the assembly with the application entry point + /// + public string entryAssembly { get; set; } + + /// + /// Gets the set of resources needed to boot the application. This includes the transitive + /// closure of .NET assemblies (including the entrypoint assembly), the dotnet.wasm file, + /// and any PDBs to be loaded. + /// + /// Within , dictionary keys are resource names, + /// and values are SHA-256 hashes formatted in prefixed base-64 style (e.g., 'sha256-abcdefg...') + /// as used for subresource integrity checking. + /// + public ResourcesData resources { get; set; } = new ResourcesData(); + + /// + /// Gets a value that determines whether to enable caching of the + /// inside a CacheStorage instance within the browser. + /// + public bool cacheBootResources { get; set; } + + /// + /// Gets a value that determines if this is a debug build. + /// + public bool debugBuild { get; set; } + + /// + /// Gets a value that determines if the linker is enabled. + /// + public bool linkerEnabled { get; set; } + + /// + /// Config files for the application + /// + public List config { get; set; } + } + + public class ResourcesData + { + /// + /// .NET Wasm runtime resources (dotnet.wasm, dotnet.js) etc. + /// + public ResourceHashesByNameDictionary runtime { get; set; } = new ResourceHashesByNameDictionary(); + + /// + /// "assembly" (.dll) resources + /// + public ResourceHashesByNameDictionary assembly { get; set; } = new ResourceHashesByNameDictionary(); + + /// + /// "debug" (.pdb) resources + /// + [DataMember(EmitDefaultValue = false)] + public ResourceHashesByNameDictionary pdb { get; set; } + + /// + /// localization (.satellite resx) resources + /// + [DataMember(EmitDefaultValue = false)] + public Dictionary satelliteResources { get; set; } + + /// + /// Assembly (.dll) resources that are loaded lazily during runtime + /// + [DataMember(EmitDefaultValue = false)] + public ResourceHashesByNameDictionary lazyAssembly { get; set; } + } +#pragma warning restore IDE1006 // Naming Styles +} diff --git a/src/Components/WebAssembly/Sdk/src/BrotliCompress.cs b/src/Components/WebAssembly/Sdk/src/BrotliCompress.cs new file mode 100644 index 0000000000..89b3004a5e --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/BrotliCompress.cs @@ -0,0 +1,125 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class BrotliCompress : ToolTask + { + private static readonly char[] InvalidPathChars = Path.GetInvalidFileNameChars(); + private string _dotnetPath; + + [Required] + public ITaskItem[] FilesToCompress { get; set; } + + [Output] + public ITaskItem[] CompressedFiles { get; set; } + + [Required] + public string OutputDirectory { get; set; } + + public string CompressionLevel { get; set; } + + public bool SkipIfOutputIsNewer { get; set; } + + [Required] + public string ToolAssembly { get; set; } + + protected override string ToolName => Path.GetDirectoryName(DotNetPath); + + private string DotNetPath + { + get + { + if (!string.IsNullOrEmpty(_dotnetPath)) + { + return _dotnetPath; + } + + _dotnetPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); + if (string.IsNullOrEmpty(_dotnetPath)) + { + throw new InvalidOperationException("DOTNET_HOST_PATH is not set"); + } + + return _dotnetPath; + } + } + + protected override string GenerateCommandLineCommands() => ToolAssembly; + + protected override string GenerateResponseFileCommands() + { + var builder = new StringBuilder(); + + + builder.AppendLine("brotli"); + + if (!string.IsNullOrEmpty(CompressionLevel)) + { + builder.AppendLine("-c"); + builder.AppendLine(CompressionLevel); + } + + CompressedFiles = new ITaskItem[FilesToCompress.Length]; + + for (var i = 0; i < FilesToCompress.Length; i++) + { + var input = FilesToCompress[i]; + var inputFullPath = input.GetMetadata("FullPath"); + var relativePath = input.GetMetadata("RelativePath"); + var outputRelativePath = Path.Combine(OutputDirectory, CalculateTargetPath(inputFullPath, ".br")); + var outputFullPath = Path.GetFullPath(outputRelativePath); + + var outputItem = new TaskItem(outputRelativePath); + outputItem.SetMetadata("RelativePath", relativePath + ".br"); + CompressedFiles[i] = outputItem; + + if (SkipIfOutputIsNewer && File.Exists(outputFullPath) && File.GetLastWriteTimeUtc(inputFullPath) < File.GetLastWriteTimeUtc(outputFullPath)) + { + Log.LogMessage(MessageImportance.Low, $"Skipping compression for '{input.ItemSpec}' because '{outputRelativePath}' is newer than '{input.ItemSpec}'."); + continue; + } + + builder.AppendLine("-s"); + builder.AppendLine(inputFullPath); + + builder.AppendLine("-o"); + builder.AppendLine(outputFullPath); + } + + return builder.ToString(); + } + + internal static string CalculateTargetPath(string relativePath, string extension) + { + // RelativePath can be long and if used as-is to write the output, might result in long path issues on Windows. + // Instead we'll calculate a fixed length path by hashing the input file name. This uses SHA1 similar to the Hash task in MSBuild + // since it has no crytographic significance. + using var hash = SHA1.Create(); + var bytes = Encoding.UTF8.GetBytes(relativePath); + var hashString = Convert.ToBase64String(hash.ComputeHash(bytes)); + + var builder = new StringBuilder(); + + for (var i = 0; i < 8; i++) + { + var c = hashString[i]; + builder.Append(InvalidPathChars.Contains(c) ? '+' : c); + } + + builder.Append(extension); + return builder.ToString(); + } + + protected override string GenerateFullPathToTool() => DotNetPath; + } +} diff --git a/src/Components/WebAssembly/Sdk/src/CreateBlazorTrimmerRootDescriptorFile.cs b/src/Components/WebAssembly/Sdk/src/CreateBlazorTrimmerRootDescriptorFile.cs new file mode 100644 index 0000000000..f14ffad221 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/CreateBlazorTrimmerRootDescriptorFile.cs @@ -0,0 +1,79 @@ +// 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.Xml; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + // Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/CreateRootDescriptorFile.cs + public class CreateBlazorTrimmerRootDescriptorFile : Task + { + [Required] + public ITaskItem[] Assemblies { get; set; } + + [Required] + public ITaskItem TrimmerFile { get; set; } + + public override bool Execute() + { + var rootDescriptor = CreateRootDescriptorContents(); + if (File.Exists(TrimmerFile.ItemSpec)) + { + var existing = File.ReadAllText(TrimmerFile.ItemSpec); + + if (string.Equals(rootDescriptor, existing, StringComparison.Ordinal)) + { + Log.LogMessage(MessageImportance.Low, "Skipping write to file {0} because contents would not change.", TrimmerFile.ItemSpec); + // Avoid writing if the file contents are identical. This is required for build incrementalism. + return !Log.HasLoggedErrors; + } + } + + File.WriteAllText(TrimmerFile.ItemSpec, rootDescriptor); + return !Log.HasLoggedErrors; + } + + internal string CreateRootDescriptorContents() + { + var roots = new XElement("linker"); + foreach (var assembly in Assemblies.OrderBy(a => a.ItemSpec)) + { + // NOTE: Descriptor files don't include the file extension + // in the assemblyName. + var assemblyName = assembly.GetMetadata("FileName"); + var typePreserved = assembly.GetMetadata("Preserve"); + var typeRequired = assembly.GetMetadata("Required"); + + var attributes = new List + { + new XAttribute("fullname", "*"), + new XAttribute("required", typeRequired), + }; + + if (!string.IsNullOrEmpty(typePreserved)) + { + attributes.Add(new XAttribute("preserve", typePreserved)); + } + + roots.Add(new XElement("assembly", + new XAttribute("fullname", assemblyName), + new XElement("type", attributes))); + } + + var xmlWriterSettings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true + }; + + return new XDocument(roots).Root.ToString(); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/GZipCompress.cs b/src/Components/WebAssembly/Sdk/src/GZipCompress.cs new file mode 100644 index 0000000000..4b8a0c542a --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/GZipCompress.cs @@ -0,0 +1,70 @@ +// 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 System.IO.Compression; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class GZipCompress : Task + { + [Required] + public ITaskItem[] FilesToCompress { get; set; } + + [Output] + public ITaskItem[] CompressedFiles { get; set; } + + [Required] + public string OutputDirectory { get; set; } + + public override bool Execute() + { + CompressedFiles = new ITaskItem[FilesToCompress.Length]; + + Directory.CreateDirectory(OutputDirectory); + + System.Threading.Tasks.Parallel.For(0, FilesToCompress.Length, i => + { + var file = FilesToCompress[i]; + var inputPath = file.ItemSpec; + var relativePath = file.GetMetadata("RelativePath"); + var outputRelativePath = Path.Combine( + OutputDirectory, + BrotliCompress.CalculateTargetPath(relativePath, ".gz")); + + var outputItem = new TaskItem(outputRelativePath); + outputItem.SetMetadata("RelativePath", relativePath + ".gz"); + CompressedFiles[i] = outputItem; + + if (File.Exists(outputRelativePath) && File.GetLastWriteTimeUtc(inputPath) < File.GetLastWriteTimeUtc(outputRelativePath)) + { + // Incrementalism. If input source doesn't exist or it exists and is not newer than the expected output, do nothing. + Log.LogMessage(MessageImportance.Low, $"Skipping '{inputPath}' because '{outputRelativePath}' is newer than '{inputPath}'."); + return; + } + + try + { + using var sourceStream = File.OpenRead(inputPath); + using var fileStream = File.Create(outputRelativePath); + using var stream = new GZipStream(fileStream, CompressionLevel.Optimal); + + sourceStream.CopyTo(stream); + } + catch (Exception e) + { + Log.LogErrorFromException(e); + return; + } + }); + + return !Log.HasLoggedErrors; + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/GenerateBlazorWebAssemblyBootJson.cs b/src/Components/WebAssembly/Sdk/src/GenerateBlazorWebAssemblyBootJson.cs new file mode 100644 index 0000000000..81cb6ecb96 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/GenerateBlazorWebAssemblyBootJson.cs @@ -0,0 +1,155 @@ +// 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.Runtime.Serialization.Json; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class GenerateBlazorWebAssemblyBootJson : Task + { + [Required] + public string AssemblyPath { get; set; } + + [Required] + public ITaskItem[] Resources { get; set; } + + [Required] + public bool DebugBuild { get; set; } + + [Required] + public bool LinkerEnabled { get; set; } + + [Required] + public bool CacheBootResources { get; set; } + + public ITaskItem[] ConfigurationFiles { get; set; } + + [Required] + public string OutputPath { get; set; } + + public ITaskItem[] LazyLoadedAssemblies { get; set; } + + public override bool Execute() + { + using var fileStream = File.Create(OutputPath); + var entryAssemblyName = AssemblyName.GetAssemblyName(AssemblyPath).Name; + + try + { + WriteBootJson(fileStream, entryAssemblyName); + } + catch (Exception ex) + { + Log.LogErrorFromException(ex); + } + + return !Log.HasLoggedErrors; + } + + // Internal for tests + public void WriteBootJson(Stream output, string entryAssemblyName) + { + var result = new BootJsonData + { + entryAssembly = entryAssemblyName, + cacheBootResources = CacheBootResources, + debugBuild = DebugBuild, + linkerEnabled = LinkerEnabled, + resources = new ResourcesData(), + config = new List(), + }; + + // Build a two-level dictionary of the form: + // - assembly: + // - UriPath (e.g., "System.Text.Json.dll") + // - ContentHash (e.g., "4548fa2e9cf52986") + // - runtime: + // - UriPath (e.g., "dotnet.js") + // - ContentHash (e.g., "3448f339acf512448") + if (Resources != null) + { + var resourceData = result.resources; + foreach (var resource in Resources) + { + ResourceHashesByNameDictionary resourceList; + + var fileName = resource.GetMetadata("FileName"); + var extension = resource.GetMetadata("Extension"); + var resourceCulture = resource.GetMetadata("Culture"); + var assetType = resource.GetMetadata("AssetType"); + var resourceName = $"{fileName}{extension}"; + + if (IsLazyLoadedAssembly(fileName)) + { + resourceData.lazyAssembly ??= new ResourceHashesByNameDictionary(); + resourceList = resourceData.lazyAssembly; + } + else if (!string.IsNullOrEmpty(resourceCulture)) + { + resourceData.satelliteResources ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + resourceName = resourceCulture + "/" + resourceName; + + if (!resourceData.satelliteResources.TryGetValue(resourceCulture, out resourceList)) + { + resourceList = new ResourceHashesByNameDictionary(); + resourceData.satelliteResources.Add(resourceCulture, resourceList); + } + } + else if (string.Equals(extension, ".pdb", StringComparison.OrdinalIgnoreCase)) + { + resourceData.pdb ??= new ResourceHashesByNameDictionary(); + resourceList = resourceData.pdb; + } + else if (string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase)) + { + resourceList = resourceData.assembly; + } + else if (string.Equals(assetType, "native", StringComparison.OrdinalIgnoreCase)) + { + resourceList = resourceData.runtime; + } + else + { + // This should include items such as XML doc files, which do not need to be recorded in the manifest. + continue; + } + + if (!resourceList.ContainsKey(resourceName)) + { + resourceList.Add(resourceName, $"sha256-{resource.GetMetadata("FileHash")}"); + } + } + } + + if (ConfigurationFiles != null) + { + foreach (var configFile in ConfigurationFiles) + { + result.config.Add(Path.GetFileName(configFile.ItemSpec)); + } + } + + var serializer = new DataContractJsonSerializer(typeof(BootJsonData), new DataContractJsonSerializerSettings + { + UseSimpleDictionaryFormat = true + }); + + using var writer = JsonReaderWriterFactory.CreateJsonWriter(output, Encoding.UTF8, ownsStream: false, indent: true); + serializer.WriteObject(writer, result); + } + + private bool IsLazyLoadedAssembly(string fileName) + { + return LazyLoadedAssemblies != null && LazyLoadedAssemblies.Any(a => a.ItemSpec == fileName); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/GenerateServiceWorkerAssetsManifest.cs b/src/Components/WebAssembly/Sdk/src/GenerateServiceWorkerAssetsManifest.cs new file mode 100644 index 0000000000..08c75a927a --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/GenerateServiceWorkerAssetsManifest.cs @@ -0,0 +1,97 @@ +// 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 System.Linq; +using System.Runtime.Serialization.Json; +using System.Security.Cryptography; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public partial class GenerateServiceWorkerAssetsManifest : Task + { + [Required] + public ITaskItem[] Assets { get; set; } + + public string Version { get; set; } + + [Required] + public string OutputPath { get; set; } + + [Output] + public string CalculatedVersion { get; set; } + + public override bool Execute() + { + using var fileStream = File.Create(OutputPath); + CalculatedVersion = GenerateAssetManifest(fileStream); + + return true; + } + + internal string GenerateAssetManifest(Stream stream) + { + var assets = new AssetsManifestFileEntry[Assets.Length]; + System.Threading.Tasks.Parallel.For(0, assets.Length, i => + { + var item = Assets[i]; + var hash = item.GetMetadata("FileHash"); + var url = item.GetMetadata("AssetUrl"); + + if (string.IsNullOrEmpty(hash)) + { + // Some files that are part of the service worker manifest may not have their hashes previously + // calcualted. Calculate them at this time. + using var sha = SHA256.Create(); + using var file = File.OpenRead(item.ItemSpec); + var bytes = sha.ComputeHash(file); + + hash = Convert.ToBase64String(bytes); + } + + assets[i] = new AssetsManifestFileEntry + { + hash = "sha256-" + hash, + url = url, + }; + }); + + var version = Version; + if (string.IsNullOrEmpty(version)) + { + // If a version isn't specified (which is likely the most common case), construct a Version by combining + // the file names + hashes of all the inputs. + + var combinedHash = string.Join( + Environment.NewLine, + assets.OrderBy(f => f.url, StringComparer.Ordinal).Select(f => f.hash)); + + using var sha = SHA256.Create(); + var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(combinedHash)); + version = Convert.ToBase64String(bytes).Substring(0, 8); + } + + var data = new AssetsManifestFile + { + version = version, + assets = assets, + }; + + using var streamWriter = new StreamWriter(stream, Encoding.UTF8, bufferSize: 50, leaveOpen: true); + streamWriter.Write("self.assetsManifest = "); + streamWriter.Flush(); + + using var jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, ownsStream: false, indent: true); + new DataContractJsonSerializer(typeof(AssetsManifestFile)).WriteObject(jsonWriter, data); + jsonWriter.Flush(); + + streamWriter.WriteLine(";"); + + return version; + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.csproj b/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.csproj new file mode 100644 index 0000000000..adf7330a3f --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.csproj @@ -0,0 +1,89 @@ + + + MSBuild support for building Blazor WebAssembly apps. + $(DefaultNetCoreTargetFramework);net46 + + Microsoft.NET.Sdk.BlazorWebAssembly.Tasks + $(MSBuildProjectName).nuspec + true + $(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly\$(Configuration)\sdk-output\ + + + $(NoWarn);NU5100 + + false + + + + + + + + + + + + + + + + + + <_ContinueOnError>true + <_Retries>1 + + + + <_ContinueOnError>false + <_Retries>10 + + + + <_WebAssemblyToolsOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly.Tools\$(Configuration)\$(DefaultNetCoreTargetFramework)\publish\Microsoft.*" /> + + + + + + + + + + + + + + + + + + + + + $(PackageTags.Replace(';',' ')) + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.nuspec b/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.nuspec new file mode 100644 index 0000000000..6d74a1931c --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.nuspec @@ -0,0 +1,19 @@ + + + + $CommonMetadataElements$ + + + + + + + $CommonFileElements$ + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.props b/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.props new file mode 100644 index 0000000000..3f6870441f --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.props @@ -0,0 +1,22 @@ + + + + true + + + + <_BlazorWebAssemblyPropsFile Condition="'$(_BlazorWebAssemblyPropsFile)' == ''">$(MSBuildThisFileDirectory)..\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.props + + + + diff --git a/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.targets b/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.targets new file mode 100644 index 0000000000..616c56fb8a --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.targets @@ -0,0 +1,20 @@ + + + + + <_BlazorWebAssemblyTargetsFile Condition="'$(_BlazorWebAssemblyTargetsFile)' == ''">$(MSBuildThisFileDirectory)..\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets + + + + + diff --git a/src/Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js b/src/Components/WebAssembly/Sdk/src/_._ similarity index 100% rename from src/Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js rename to src/Components/WebAssembly/Sdk/src/_._ diff --git a/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.props b/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.props new file mode 100644 index 0000000000..27e3fde3e0 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.props @@ -0,0 +1,16 @@ + + + + <_BlazorWebAssemblyPropsFile>$(MSBuildThisFileDirectory)..\..\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.props + + diff --git a/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.targets b/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.targets new file mode 100644 index 0000000000..8091b3d876 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.targets @@ -0,0 +1,16 @@ + + + + <_BlazorWebAssemblyTargetsFile>$(MSBuildThisFileDirectory)..\..\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets + + diff --git a/src/Components/WebAssembly/Sdk/src/targets/BlazorWasm.web.config b/src/Components/WebAssembly/Sdk/src/targets/BlazorWasm.web.config new file mode 100644 index 0000000000..7f9995d792 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/BlazorWasm.web.config @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/src/targets/LinkerWorkaround.xml b/src/Components/WebAssembly/Sdk/src/targets/LinkerWorkaround.xml new file mode 100644 index 0000000000..c61bc7a3cc --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/LinkerWorkaround.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.props b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.props new file mode 100644 index 0000000000..03b94ad566 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.props @@ -0,0 +1,36 @@ + + + + + browser-wasm + + + false + + exe + + false + + false + + + + + <_RazorSdkImportsMicrosoftNetSdkRazor Condition="'$(UsingMicrosoftNETSdkRazor)' != 'true'">true + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets new file mode 100644 index 0000000000..a11bc00625 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets @@ -0,0 +1,582 @@ + + + + + true + + + + + + + + + + + + + + $(MSBuildThisFileDirectory)..\..\ + <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net5.0 + <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net46 + <_BlazorWebAssemblySdkTasksAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tasks\$(_BlazorWebAssemblySdkTasksTFM)\Microsoft.NET.Sdk.BlazorWebAssembly.Tasks.dll + <_BlazorWebAssemblySdkToolAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\net5.0\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.dll + + + + + + + + + + + true + true + + + true + link + + / + true + + + false + false + false + false + true + + + <_BlazorOutputPath>wwwroot\_framework\ + + + + + + + + + <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion) + <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion) + <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js + <_BlazorDotNetJsFilePath>$(IntermediateOutputPath)$(_BlazorDotnetJsFileName) + + + + <_DotNetJsItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.DestinationSubPath)' == 'dotnet.js' AND '%(ReferenceCopyLocalPaths.AssetType)' == 'native'" /> + + + + + + + + + + + + + + + + <_BlazorSatelliteAssemblyCacheFile>$(IntermediateOutputPath)blazor.satelliteasm.props + + $(OutputPath)$(PublishDirName)\ + + + + <_BlazorJSFile Include="$(BlazorWebAssemblyJSPath)" /> + <_BlazorJSFile Include="$(BlazorWebAssemblyJSMapPath)" Condition="Exists('$(BlazorWebAssemblyJSMapPath)')" /> + + <_BlazorConfigFile Include="wwwroot\appsettings*.json" /> + + + + + + + <_BlazorCopyLocalPath + Include="@(ReferenceCopyLocalPaths)" + Exclude="@(ReferenceSatellitePaths)"/> + + <_BlazorCopyLocalPath Include="@(IntermediateSatelliteAssembliesWithTargetPath)"> + %(IntermediateSatelliteAssembliesWithTargetPath.Culture)\ + + + <_BlazorOutputWithTargetPath Include=" + @(_BlazorCopyLocalPath); + @(IntermediateAssembly); + @(_DebugSymbolsIntermediatePath); + @(_BlazorJSFile)" /> + + <_BlazorOutputWithTargetPath Include="@(ReferenceSatellitePaths)"> + $([System.String]::Copy('%(ReferenceSatellitePaths.DestinationSubDirectory)').Trim('\').Trim('/')) + + + + + + + + + + + <_BlazorOutputWithTargetPath + Include="@(_BlazorReadSatelliteAssembly)" + Exclude="@(_BlazorOutputWithTargetPath)" + Condition="'@(_BlazorReadSatelliteAssembly->Count())' != '0'" /> + + + <_BlazorOutputWithTargetPath + TargetPath="$(_BlazorOutputPath)%(_BlazorOutputWithTargetPath.DestinationSubDirectory)%(FileName)%(Extension)" + Condition="'%(__BlazorOutputWithTargetPath.TargetPath)' == ''" /> + + + + + + <_BlazorBuildGZipCompressDirectory>$(IntermediateOutputPath)build-gz\ + + + + + <_GzipFileToCompressForBuild + Include="@(ReferenceCopyLocalPaths)" + RelativePath="$(_BlazorOutputPath)%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)" + Condition="'%(Extension)' == '.dll' or '%(ReferenceCopyLocalPaths.AssetType)' == 'native'" /> + + + + + + + + + + <_BlazorWriteSatelliteAssembly Include="@(_BlazorOutputWithTargetPath->HasMetadata('Culture'))" /> + + + + + + + + + + + + + + + + + + + + + + + + + + _BlazorWasmPrepareForRun; + $(PrepareForRunDependsOn) + + + + $(GetCurrentProjectStaticWebAssetsDependsOn); + _BlazorWasmPrepareForRun; + + + + + + <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json + + + + + + + + + + <_BlazorWebAssemblyStaticWebAsset Include="$(_BlazorBuildBootJsonPath)"> + $(PackageId) + + $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) + $(StaticWebAssetBasePath) + _framework/blazor.boot.json + Never + + + <_BlazorWebAssemblyStaticWebAsset Include="@(_BlazorOutputWithHash)"> + $(PackageId) + + $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) + $(StaticWebAssetBasePath) + $([System.String]::Copy('%(_BlazorOutputWithHash.TargetPath)').Replace('\','/').Substring(8)) + Never + + + <_BlazorWebAssemblyStaticWebAsset Include="@(_BlazorBuildGZipCompressedFile)"> + $(PackageId) + + $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) + $(StaticWebAssetBasePath) + $([System.String]::Copy('%(_BlazorBuildGZipCompressedFile.RelativePath)').Replace('\','/').Substring(8)) + Never + + + + <_ExternalStaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" SourceType="Generated" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml + + + + <_BlazorTypeGranularAssembly + Include="@(ManagedAssemblyToLink)" + Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))"> + false + all + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_BlazorPublishBootResource + Include="@(ResolvedFileToPublish)" + Condition="$([System.String]::Copy('%(RelativePath)').Replace('\','/').StartsWith('wwwroot/_framework')) AND '%(Extension)' != '.a'" /> + + + + + + + + + + + + + + + + + + <_CompressedFileOutputPath>$(IntermediateOutputPath)compress\ + <_BlazorWebAssemblyBrotliIncremental>true + + + + <_FileToCompress + Include="@(ResolvedFileToPublish)" + Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))" /> + + + + + + + + <_DotNetHostDirectory>$(NetCoreRoot) + <_DotNetHostFileName>dotnet + <_DotNetHostFileName Condition="'$(OS)' == 'Windows_NT'">dotnet.exe + + + + + + + + + + + + + + + + + + + + + + + + <_PublishingBlazorWasmProject>true + + + + + + + + + + + + + + %(RelativePath) + PreserveNewest + + + + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets new file mode 100644 index 0000000000..5202eb338d --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets @@ -0,0 +1,169 @@ + + + + + + + + + <_ServiceWorkerAssetsManifestIntermediateOutputPath Condition="'$([System.IO.Path]::IsPathRooted($(BaseIntermediateOutputPath)))' == 'true'">obj\$(Configuration)\$(TargetFramework)\$(ServiceWorkerAssetsManifest) + <_ServiceWorkerAssetsManifestIntermediateOutputPath Condition="'$([System.IO.Path]::IsPathRooted($(BaseIntermediateOutputPath)))' != 'true'">$(IntermediateOutputPath)$(ServiceWorkerAssetsManifest) + <_ServiceWorkerAssetsManifestFullPath>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/$(_ServiceWorkerAssetsManifestIntermediateOutputPath)')) + + + + <_ManifestStaticWebAsset Include="$(_ServiceWorkerAssetsManifestFullPath)"> + + $(PackageId) + $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) + $(StaticWebAssetBasePath) + $(ServiceWorkerAssetsManifest) + Never + + + + <_ServiceWorkerIntermediateFile Include="@(ServiceWorker->'$(IntermediateOutputPath)serviceworkers\%(Identity)')"> + %(ServiceWorker.PublishedContent) + %(ServiceWorker.Identity) + %(ServiceWorker.Identity) + %(ServiceWorker.Identity) + $([System.String]::Copy('%(ServiceWorker.Identity)').Substring(8)) + + + <_ServiceWorkerStaticWebAsset Include="%(_ServiceWorkerIntermediateFile.FullPath)"> + + $(PackageId) + $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) + $(StaticWebAssetBasePath) + %(TargetOutputPath) + Never + + + + + + + + + + + <_ServiceWorkItem Include="@(StaticWebAsset)" Exclude="$(_ServiceWorkerAssetsManifestFullPath);@(_ServiceWorkerStaticWebAsset)"> + $([System.String]::Copy('$([System.String]::Copy('%(StaticWebAsset.BasePath)').TrimEnd('/'))/%(StaticWebAsset.RelativePath)').Replace('\','/').TrimStart('/')) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ServiceWorkerAssetsManifestPublishIntermediateOutputPath>$(IntermediateOutputPath)publish-$(ServiceWorkerAssetsManifest) + + + + <_ServiceWorkerIntermediatePublishFile Include="$(IntermediateOutputPath)serviceworkers\%(FileName).publish%(Extension)"> + %(ServiceWorker.PublishedContent) + %(ServiceWorker.Identity) + %(ServiceWorker.Identity) + + + <_ServiceWorkerPublishFile Include="@(ResolvedFileToPublish)" Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))"> + $([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').TrimStart('/')) + $([System.String]::Copy('%(RelativePath)').Replace('\','/').Substring(8)) + + + + + + + + + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/test/BlazorReadSatelliteAssemblyFileTest.cs b/src/Components/WebAssembly/Sdk/test/BlazorReadSatelliteAssemblyFileTest.cs new file mode 100644 index 0000000000..b95a6154b6 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/test/BlazorReadSatelliteAssemblyFileTest.cs @@ -0,0 +1,68 @@ +// 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 Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Moq; +using Xunit; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class BlazorReadSatelliteAssemblyFileTest + { + [Fact] + public void WritesAndReadsRoundTrip() + { + // Arrange/Act + var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + var writer = new BlazorWriteSatelliteAssemblyFile + { + BuildEngine = Mock.Of(), + WriteFile = new TaskItem(tempFile), + SatelliteAssembly = new[] + { + new TaskItem("Resources.fr.dll", new Dictionary + { + ["Culture"] = "fr", + ["DestinationSubDirectory"] = "fr\\", + }), + new TaskItem("Resources.ja-jp.dll", new Dictionary + { + ["Culture"] = "ja-jp", + ["DestinationSubDirectory"] = "ja-jp\\", + }), + }, + }; + + var reader = new BlazorReadSatelliteAssemblyFile + { + BuildEngine = Mock.Of(), + ReadFile = new TaskItem(tempFile), + }; + + writer.Execute(); + + Assert.True(File.Exists(tempFile), "Write should have succeeded."); + + reader.Execute(); + + Assert.Collection( + reader.SatelliteAssembly, + assembly => + { + Assert.Equal("Resources.fr.dll", assembly.ItemSpec); + Assert.Equal("fr", assembly.GetMetadata("Culture")); + Assert.Equal("fr\\", assembly.GetMetadata("DestinationSubDirectory")); + }, + assembly => + { + Assert.Equal("Resources.ja-jp.dll", assembly.ItemSpec); + Assert.Equal("ja-jp", assembly.GetMetadata("Culture")); + Assert.Equal("ja-jp\\", assembly.GetMetadata("DestinationSubDirectory")); + }); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/test/GenerateBlazorBootJsonTest.cs b/src/Components/WebAssembly/Sdk/test/GenerateBlazorBootJsonTest.cs new file mode 100644 index 0000000000..139f22f27f --- /dev/null +++ b/src/Components/WebAssembly/Sdk/test/GenerateBlazorBootJsonTest.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.Linq; +using System.Runtime.Serialization.Json; +using Microsoft.Build.Framework; +using Moq; +using Xunit; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class GenerateBlazorWebAssemblyBootJsonTest + { + [Fact] + public void GroupsResourcesByType() + { + // Arrange + var taskInstance = new GenerateBlazorWebAssemblyBootJson + { + AssemblyPath = "MyApp.Entrypoint.dll", + Resources = new[] + { + CreateResourceTaskItem( + ("FileName", "My.Assembly1"), + ("Extension", ".dll"), + ("FileHash", "abcdefghikjlmnopqrstuvwxyz")), + + CreateResourceTaskItem( + ("FileName", "My.Assembly2"), + ("Extension", ".dll"), + ("FileHash", "012345678901234567890123456789")), + + CreateResourceTaskItem( + ("FileName", "SomePdb"), + ("Extension", ".pdb"), + ("FileHash", "pdbhashpdbhashpdbhash")), + + CreateResourceTaskItem( + ("FileName", "My.Assembly1"), + ("Extension", ".pdb"), + ("FileHash", "pdbdefghikjlmnopqrstuvwxyz")), + + CreateResourceTaskItem( + ("FileName", "some-runtime-file"), + ("FileHash", "runtimehashruntimehash"), + ("AssetType", "native")), + + CreateResourceTaskItem( + ("FileName", "satellite-assembly1"), + ("Extension", ".dll"), + ("FileHash", "hashsatelliteassembly1"), + ("Culture", "en-GB")), + + CreateResourceTaskItem( + ("FileName", "satellite-assembly2"), + ("Extension", ".dll"), + ("FileHash", "hashsatelliteassembly2"), + ("Culture", "fr")), + + CreateResourceTaskItem( + ("FileName", "satellite-assembly3"), + ("Extension", ".dll"), + ("FileHash", "hashsatelliteassembly3"), + ("Culture", "en-GB")), + } + }; + + using var stream = new MemoryStream(); + + // Act + taskInstance.WriteBootJson(stream, "MyEntrypointAssembly"); + + // Assert + var parsedContent = ParseBootData(stream); + Assert.Equal("MyEntrypointAssembly", parsedContent.entryAssembly); + + var resources = parsedContent.resources.assembly; + Assert.Equal(2, resources.Count); + Assert.Equal("sha256-abcdefghikjlmnopqrstuvwxyz", resources["My.Assembly1.dll"]); + Assert.Equal("sha256-012345678901234567890123456789", resources["My.Assembly2.dll"]); + + resources = parsedContent.resources.pdb; + Assert.Equal(2, resources.Count); + Assert.Equal("sha256-pdbhashpdbhashpdbhash", resources["SomePdb.pdb"]); + Assert.Equal("sha256-pdbdefghikjlmnopqrstuvwxyz", resources["My.Assembly1.pdb"]); + + resources = parsedContent.resources.runtime; + Assert.Single(resources); + Assert.Equal("sha256-runtimehashruntimehash", resources["some-runtime-file"]); + + var satelliteResources = parsedContent.resources.satelliteResources; + Assert.Collection( + satelliteResources.OrderBy(kvp => kvp.Key), + kvp => + { + Assert.Equal("en-GB", kvp.Key); + Assert.Collection( + kvp.Value.OrderBy(item => item.Key), + item => + { + Assert.Equal("en-GB/satellite-assembly1.dll", item.Key); + Assert.Equal("sha256-hashsatelliteassembly1", item.Value); + }, + item => + { + Assert.Equal("en-GB/satellite-assembly3.dll", item.Key); + Assert.Equal("sha256-hashsatelliteassembly3", item.Value); + }); + }, + kvp => + { + Assert.Equal("fr", kvp.Key); + Assert.Collection( + kvp.Value.OrderBy(item => item.Key), + item => + { + Assert.Equal("fr/satellite-assembly2.dll", item.Key); + Assert.Equal("sha256-hashsatelliteassembly2", item.Value); + }); + }); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CanSpecifyCacheBootResources(bool flagValue) + { + // Arrange + var taskInstance = new GenerateBlazorWebAssemblyBootJson { CacheBootResources = flagValue }; + using var stream = new MemoryStream(); + + // Act + taskInstance.WriteBootJson(stream, "MyEntrypointAssembly"); + + // Assert + var parsedContent = ParseBootData(stream); + Assert.Equal(flagValue, parsedContent.cacheBootResources); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CanSpecifyDebugBuild(bool flagValue) + { + // Arrange + var taskInstance = new GenerateBlazorWebAssemblyBootJson { DebugBuild = flagValue }; + using var stream = new MemoryStream(); + + // Act + taskInstance.WriteBootJson(stream, "MyEntrypointAssembly"); + + // Assert + var parsedContent = ParseBootData(stream); + Assert.Equal(flagValue, parsedContent.debugBuild); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CanSpecifyLinkerEnabled(bool flagValue) + { + // Arrange + var taskInstance = new GenerateBlazorWebAssemblyBootJson { LinkerEnabled = flagValue }; + using var stream = new MemoryStream(); + + // Act + taskInstance.WriteBootJson(stream, "MyEntrypointAssembly"); + + // Assert + var parsedContent = ParseBootData(stream); + Assert.Equal(flagValue, parsedContent.linkerEnabled); + } + + private static BootJsonData ParseBootData(Stream stream) + { + stream.Position = 0; + var serializer = new DataContractJsonSerializer( + typeof(BootJsonData), + new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true }); + return (BootJsonData)serializer.ReadObject(stream); + } + + private static ITaskItem CreateResourceTaskItem(params (string key, string value)[] values) + { + var mock = new Mock(); + + foreach (var (key, value) in values) + { + mock.Setup(m => m.GetMetadata(key)).Returns(value); + } + return mock.Object; + } + } +} diff --git a/src/Components/WebAssembly/Sdk/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj b/src/Components/WebAssembly/Sdk/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj new file mode 100644 index 0000000000..5d30b4f781 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj @@ -0,0 +1,12 @@ + + + + $(DefaultNetCoreTargetFramework) + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props new file mode 100644 index 0000000000..6d0949542f --- /dev/null +++ b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props @@ -0,0 +1,50 @@ + + + + + + false + true + + + false + + net5.0 + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, 'AspNetCore.sln'))\ + + $(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\build\netstandard2.0\Sdk.Razor.CurrentVersion.props + $(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\build\netstandard2.0\Sdk.Razor.CurrentVersion.targets + $(RepoRoot)artifacts\bin\Microsoft.NET.Sdk.Razor\ + $(RepoRoot)artifacts\bin\Microsoft.NET.Sdk.BlazorWebAssembly\ + $(MSBuildThisFileDirectory)blazor.webassembly.js + + + + + + + 1.0.0 + + + <_BlazorBrotliCompressionLevel>NoCompression + + + + + <_MvcAssemblyName Include="Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib" /> + + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/testassets/Directory.Build.targets b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.targets new file mode 100644 index 0000000000..45a2ee1e56 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.targets @@ -0,0 +1,7 @@ + + + $(MicrosoftNETCoreAppRuntimeVersion) + + 99.9 + + diff --git a/src/Razor/test/testassets/LinkBaseToWebRoot/js/LinkedScript.js b/src/Components/WebAssembly/Sdk/testassets/LinkBaseToWebRoot/js/LinkedScript.js similarity index 100% rename from src/Razor/test/testassets/LinkBaseToWebRoot/js/LinkedScript.js rename to src/Components/WebAssembly/Sdk/testassets/LinkBaseToWebRoot/js/LinkedScript.js diff --git a/src/Components/WebAssembly/Sdk/testassets/RestoreBlazorWasmTestProjects/RestoreBlazorWasmTestProjects.csproj b/src/Components/WebAssembly/Sdk/testassets/RestoreBlazorWasmTestProjects/RestoreBlazorWasmTestProjects.csproj new file mode 100644 index 0000000000..f4debf8088 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/testassets/RestoreBlazorWasmTestProjects/RestoreBlazorWasmTestProjects.csproj @@ -0,0 +1,12 @@ + + + $(DefaultNetCoreTargetFramework) + + + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/testassets/blazor.webassembly.js b/src/Components/WebAssembly/Sdk/testassets/blazor.webassembly.js new file mode 100644 index 0000000000..84362ca046 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/testassets/blazor.webassembly.js @@ -0,0 +1 @@ +Test file \ No newline at end of file diff --git a/src/Razor/test/testassets/blazorhosted-rid/Program.cs b/src/Components/WebAssembly/Sdk/testassets/blazorhosted-rid/Program.cs similarity index 100% rename from src/Razor/test/testassets/blazorhosted-rid/Program.cs rename to src/Components/WebAssembly/Sdk/testassets/blazorhosted-rid/Program.cs diff --git a/src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj b/src/Components/WebAssembly/Sdk/testassets/blazorhosted-rid/blazorhosted-rid.csproj similarity index 100% rename from src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj rename to src/Components/WebAssembly/Sdk/testassets/blazorhosted-rid/blazorhosted-rid.csproj diff --git a/src/Razor/test/testassets/blazorhosted/Program.cs b/src/Components/WebAssembly/Sdk/testassets/blazorhosted/Program.cs similarity index 100% rename from src/Razor/test/testassets/blazorhosted/Program.cs rename to src/Components/WebAssembly/Sdk/testassets/blazorhosted/Program.cs diff --git a/src/Razor/test/testassets/blazorhosted/blazorhosted.csproj b/src/Components/WebAssembly/Sdk/testassets/blazorhosted/blazorhosted.csproj similarity index 100% rename from src/Razor/test/testassets/blazorhosted/blazorhosted.csproj rename to src/Components/WebAssembly/Sdk/testassets/blazorhosted/blazorhosted.csproj diff --git a/src/Razor/test/testassets/blazorwasm/App.razor b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/App.razor similarity index 100% rename from src/Razor/test/testassets/blazorwasm/App.razor rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/App.razor diff --git a/src/Razor/test/testassets/blazorwasm/LinkToWebRoot/css/app.css b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/LinkToWebRoot/css/app.css similarity index 100% rename from src/Razor/test/testassets/blazorwasm/LinkToWebRoot/css/app.css rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/LinkToWebRoot/css/app.css diff --git a/src/Razor/test/testassets/blazorwasm/Pages/Index.razor b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/Pages/Index.razor similarity index 100% rename from src/Razor/test/testassets/blazorwasm/Pages/Index.razor rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/Pages/Index.razor diff --git a/src/Razor/test/testassets/blazorwasm/Program.cs b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/Program.cs similarity index 100% rename from src/Razor/test/testassets/blazorwasm/Program.cs rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/Program.cs diff --git a/src/Razor/test/testassets/blazorwasm/Resources.ja.resx.txt b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/Resources.ja.resx.txt similarity index 100% rename from src/Razor/test/testassets/blazorwasm/Resources.ja.resx.txt rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/Resources.ja.resx.txt diff --git a/src/Razor/test/testassets/blazorwasm/_Imports.razor b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/_Imports.razor similarity index 100% rename from src/Razor/test/testassets/blazorwasm/_Imports.razor rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/_Imports.razor diff --git a/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/blazorwasm.csproj similarity index 88% rename from src/Razor/test/testassets/blazorwasm/blazorwasm.csproj rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/blazorwasm.csproj index 36a511e7c7..c8906b1bd2 100644 --- a/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj +++ b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/blazorwasm.csproj @@ -1,10 +1,12 @@  + + + net5.0 - true browser-wasm - false $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\ + $(BlazorWebAssemblySdkArtifactsDirectory)$(Configuration)\sdk-output\ custom-service-worker-assets.js @@ -50,4 +52,6 @@ + + diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/Fake-License.txt b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/Fake-License.txt similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/Fake-License.txt rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/Fake-License.txt diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/css/app.css b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/css/app.css similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/css/app.css rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/css/app.css diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/index.html b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/index.html similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/index.html rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/index.html diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js diff --git a/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Class1.cs b/src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/Class1.cs similarity index 100% rename from src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Class1.cs rename to src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/Class1.cs diff --git a/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx b/src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx similarity index 100% rename from src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx rename to src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx diff --git a/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj b/src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj similarity index 100% rename from src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj rename to src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj diff --git a/src/Razor/test/testassets/razorclasslibrary/Class1.cs b/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/Class1.cs similarity index 100% rename from src/Razor/test/testassets/razorclasslibrary/Class1.cs rename to src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/Class1.cs diff --git a/src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj b/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/RazorClassLibrary.csproj similarity index 100% rename from src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj rename to src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/RazorClassLibrary.csproj diff --git a/src/Razor/test/testassets/razorclasslibrary/wwwroot/styles.css b/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/styles.css similarity index 100% rename from src/Razor/test/testassets/razorclasslibrary/wwwroot/styles.css rename to src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/styles.css diff --git a/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js b/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Components/WebAssembly/Sdk/tools/Application.cs b/src/Components/WebAssembly/Sdk/tools/Application.cs new file mode 100644 index 0000000000..d9d66d10bc --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/Application.cs @@ -0,0 +1,98 @@ +// 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.Reflection; +using System.Threading; +using Microsoft.Extensions.CommandLineUtils; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tools +{ + internal class Application : CommandLineApplication + { + public Application( + CancellationToken cancellationToken, + TextWriter output = null, + TextWriter error = null) + { + CancellationToken = cancellationToken; + Out = output ?? Out; + Error = error ?? Error; + + Name = "BlazorWebAssembly.Tools"; + FullName = "Microsoft Blazor WebAssembly SDK tool"; + Description = "CLI for Blazor WebAssembly operations."; + ShortVersionGetter = GetInformationalVersion; + + HelpOption("-?|-h|--help"); + + Commands.Add(new BrotliCompressCommand(this)); + } + + public CancellationToken CancellationToken { get; } + + public new int Execute(params string[] args) + { + try + { + return base.Execute(ExpandResponseFiles(args)); + } + catch (AggregateException ex) when (ex.InnerException != null) + { + foreach (var innerException in ex.Flatten().InnerExceptions) + { + Error.WriteLine(innerException.Message); + Error.WriteLine(innerException.StackTrace); + } + return 1; + } + catch (CommandParsingException ex) + { + // Don't show a call stack when we have unneeded arguments, just print the error message. + // The code that throws this exception will print help, so no need to do it here. + Error.WriteLine(ex.Message); + return 1; + } + catch (OperationCanceledException) + { + // This is a cancellation, not a failure. + Error.WriteLine("Cancelled"); + return 1; + } + catch (Exception ex) + { + Error.WriteLine(ex.Message); + Error.WriteLine(ex.StackTrace); + return 1; + } + } + + private string GetInformationalVersion() + { + var assembly = typeof(Application).GetTypeInfo().Assembly; + var attribute = assembly.GetCustomAttribute(); + return attribute.InformationalVersion; + } + + private static string[] ExpandResponseFiles(string[] args) + { + var expandedArgs = new List(); + foreach (var arg in args) + { + if (!arg.StartsWith("@", StringComparison.Ordinal)) + { + expandedArgs.Add(arg); + } + else + { + var fileName = arg.Substring(1); + expandedArgs.AddRange(File.ReadLines(fileName)); + } + } + + return expandedArgs.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Components/WebAssembly/Sdk/tools/BrotliCompressCommand.cs b/src/Components/WebAssembly/Sdk/tools/BrotliCompressCommand.cs new file mode 100644 index 0000000000..136c2fc660 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/BrotliCompressCommand.cs @@ -0,0 +1,85 @@ +// 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 System.IO.Compression; +using System.Threading.Tasks; +using Microsoft.Extensions.CommandLineUtils; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tools +{ + internal class BrotliCompressCommand : CommandLineApplication + { + public BrotliCompressCommand(Application parent) + : base(throwOnUnexpectedArg: true) + { + base.Parent = parent; + Name = "brotli"; + Sources = Option("-s", "files to compress", CommandOptionType.MultipleValue); + Outputs = Option("-o", "Output file path", CommandOptionType.MultipleValue); + CompressionLevelOption = Option("-c", "Compression level", CommandOptionType.SingleValue); + + Invoke = () => Execute().GetAwaiter().GetResult(); + } + + public CommandOption Sources { get; } + + public CommandOption Outputs { get; } + + public CommandOption CompressionLevelOption { get; } + + public CompressionLevel CompressionLevel { get; private set; } = CompressionLevel.Optimal; + + private Task Execute() + { + if (!ValidateArguments()) + { + ShowHelp(); + return Task.FromResult(1); + } + + return ExecuteCoreAsync(); + } + + private bool ValidateArguments() + { + if (Sources.Values.Count != Outputs.Values.Count) + { + Error.WriteLine($"{Sources.Description} has {Sources.Values.Count}, but {Outputs.Description} has {Outputs.Values.Count} values."); + return false; + } + + if (CompressionLevelOption.HasValue()) + { + if (!Enum.TryParse(CompressionLevelOption.Value(), out var value)) + { + Error.WriteLine($"Invalid option {CompressionLevelOption.Value()} for {CompressionLevelOption.Template}."); + return false; + } + + CompressionLevel = value; + } + + return true; + } + + private Task ExecuteCoreAsync() + { + Parallel.For(0, Sources.Values.Count, i => + { + var source = Sources.Values[i]; + var output = Outputs.Values[i]; + + using var sourceStream = File.OpenRead(source); + using var fileStream = new FileStream(output, FileMode.Create); + + using var stream = new BrotliStream(fileStream, CompressionLevel); + + sourceStream.CopyTo(stream); + }); + + return Task.FromResult(0); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/tools/DebugMode.cs b/src/Components/WebAssembly/Sdk/tools/DebugMode.cs new file mode 100644 index 0000000000..816bb4a783 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/DebugMode.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tools +{ + internal static class DebugMode + { + public static void HandleDebugSwitch(ref string[] args) + { + if (args.Length > 0 && string.Equals("--debug", args[0], StringComparison.OrdinalIgnoreCase)) + { + args = args.Skip(1).ToArray(); + + Console.WriteLine("Waiting for debugger in pid: {0}", Process.GetCurrentProcess().Id); + while (!Debugger.IsAttached) + { + Thread.Sleep(TimeSpan.FromSeconds(3)); + } + } + } + } +} \ No newline at end of file diff --git a/src/Components/WebAssembly/Sdk/tools/Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj b/src/Components/WebAssembly/Sdk/tools/Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj new file mode 100644 index 0000000000..6ac7a26c19 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj @@ -0,0 +1,22 @@ + + + + Exe + net5.0 + + false + false + true + + false + + + + false + + + + + + + diff --git a/src/Components/WebAssembly/Sdk/tools/Program.cs b/src/Components/WebAssembly/Sdk/tools/Program.cs new file mode 100644 index 0000000000..b00093323d --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/Program.cs @@ -0,0 +1,30 @@ +// 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 System.Threading; +using Microsoft.CodeAnalysis; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tools +{ + internal static class Program + { + public static int Main(string[] args) + { + DebugMode.HandleDebugSwitch(ref args); + + var cancel = new CancellationTokenSource(); + Console.CancelKeyPress += (sender, e) => { cancel.Cancel(); }; + + var application = new Application( + cancel.Token, + Console.Out, + Console.Error); + + application.Commands.Add(new BrotliCompressCommand(application)); + + return application.Execute(args); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/tools/runtimeconfig.template.json b/src/Components/WebAssembly/Sdk/tools/runtimeconfig.template.json new file mode 100644 index 0000000000..2c73f39890 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/runtimeconfig.template.json @@ -0,0 +1,3 @@ +{ + "rollForwardOnNoCandidateFx": 2 +} \ No newline at end of file diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj index a1e160322e..2832be128d 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj @@ -1,4 +1,4 @@ - + false + $(MSBuildProjectDirectory)\..\..\test\testassets\ + + @@ -24,49 +27,6 @@ - - - <_Parameter1>Testing.AdditionalRestoreSources - <_Parameter2>$(MSBuildThisFileDirectory)..\testassets\PregeneratedPackages - - - - <_Parameter1>ArtifactsLogDir - <_Parameter2>$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'log', '$(_BuildConfig)')) - - - - <_Parameter1>ProcDumpToolPath - <_Parameter2>$(ProcDumpPath) - - - - <_Parameter1>Testing.RepoRoot - <_Parameter2>$(RepoRoot) - - - - <_Parameter1>MicrosoftNETCoreAppRuntimeVersion - <_Parameter2>$(MicrosoftNETCoreAppRuntimeVersion) - - - - <_Parameter1>DefaultNetCoreTargetFramework - <_Parameter2>$(DefaultNetCoreTargetFramework) - - - - <_Parameter1>MicrosoftNetCompilersToolsetPackageVersion - <_Parameter2>$(MicrosoftNetCompilersToolsetPackageVersion) - - - - <_Parameter1>RazorSdkDirectoryRoot - <_Parameter2>$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\sdk-output\ - - - - diff --git a/src/Shared/E2ETesting/E2ETesting.targets b/src/Shared/E2ETesting/E2ETesting.targets index d0fa6cb856..76ced2cce9 100644 --- a/src/Shared/E2ETesting/E2ETesting.targets +++ b/src/Shared/E2ETesting/E2ETesting.targets @@ -66,14 +66,9 @@ <_DefaultProjectRoot>$([System.IO.Path]::GetFullPath($(_DefaultProjectFilter))) - <_ContentRootProjectReferencesUnfiltered - Include="@(ReferencePath)" - Condition="'%(ReferencePath.ReferenceSourceTarget)' == 'ProjectReference'" /> - <_ContentRootProjectReferencesFilter - Include="@(_ContentRootProjectReferencesUnfiltered->StartsWith('$(_DefaultProjectRoot)'))" /> <_ContentRootProjectReferences - Include="@(_ContentRootProjectReferencesFilter)" - Condition="'%(Identity)' == 'True'" /> + Include="@(ReferencePath)" + Condition="'%(ReferencePath.ReferenceSourceTarget)' == 'ProjectReference' AND $([System.String]::Copy(%(ReferencePath.MSBuildSourceProjectFile)).StartsWith('$(_DefaultProjectRoot)'))" /> diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs b/src/Shared/MSBuild.Testing/Assert.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs rename to src/Shared/MSBuild.Testing/Assert.cs diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs b/src/Shared/MSBuild.Testing/BuildVariables.cs similarity index 83% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs rename to src/Shared/MSBuild.Testing/BuildVariables.cs index 797d244625..b659ba918c 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs +++ b/src/Shared/MSBuild.Testing/BuildVariables.cs @@ -19,8 +19,12 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests public static string RazorSdkDirectoryRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "RazorSdkDirectoryRoot").Value; + public static string BlazorWebAssemblySdkDirectoryRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "BlazorWebAssemblySdkDirectoryRoot").Value; + public static string RepoRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "Testing.RepoRoot").Value; - + public static string DefaultNetCoreTargetFramework => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "DefaultNetCoreTargetFramework").Value; + + public static string TestAppsRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "TestAppsRoot").Value; } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs b/src/Shared/MSBuild.Testing/FIleThumbPrint.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs rename to src/Shared/MSBuild.Testing/FIleThumbPrint.cs diff --git a/src/Shared/MSBuild.Testing/MSBuild.Testing.targets b/src/Shared/MSBuild.Testing/MSBuild.Testing.targets new file mode 100644 index 0000000000..8868a23d97 --- /dev/null +++ b/src/Shared/MSBuild.Testing/MSBuild.Testing.targets @@ -0,0 +1,53 @@ + + + + + <_Parameter1>ArtifactsLogDir + <_Parameter2>$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'log', '$(_BuildConfig)')) + + + + <_Parameter1>ProcDumpToolPath + <_Parameter2>$(ProcDumpPath) + + + + <_Parameter1>Testing.RepoRoot + <_Parameter2>$(RepoRoot) + + + + <_Parameter1>MicrosoftNETCoreAppRuntimeVersion + <_Parameter2>$(MicrosoftNETCoreAppRuntimeVersion) + + + + <_Parameter1>DefaultNetCoreTargetFramework + <_Parameter2>$(DefaultNetCoreTargetFramework) + + + + <_Parameter1>MicrosoftNetCompilersToolsetPackageVersion + <_Parameter2>$(MicrosoftNetCompilersToolsetPackageVersion) + + + + <_Parameter1>RazorSdkDirectoryRoot + <_Parameter2>$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\sdk-output\ + + + + <_Parameter1>BlazorWebAssemblySdkDirectoryRoot + <_Parameter2>$(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly\$(Configuration)\sdk-output\ + + + + <_Parameter1>TestAppsRoot + <_Parameter2>$(TestAppsRoot) + + + + + + + \ No newline at end of file diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessKind.cs b/src/Shared/MSBuild.Testing/MSBuildProcessKind.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessKind.cs rename to src/Shared/MSBuild.Testing/MSBuildProcessKind.cs diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs b/src/Shared/MSBuild.Testing/MSBuildProcessManager.cs similarity index 98% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs rename to src/Shared/MSBuild.Testing/MSBuildProcessManager.cs index cc79523d5b..3d9ada158b 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs +++ b/src/Shared/MSBuild.Testing/MSBuildProcessManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -37,6 +37,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests $"/p:MicrosoftNETCoreAppRuntimeVersion={BuildVariables.MicrosoftNETCoreAppRuntimeVersion}", $"/p:MicrosoftNetCompilersToolsetPackageVersion={BuildVariables.MicrosoftNetCompilersToolsetPackageVersion}", $"/p:RazorSdkDirectoryRoot={BuildVariables.RazorSdkDirectoryRoot}", + $"/p:BlazorWebAssemblySdkDirectoryRoot={BuildVariables.BlazorWebAssemblySdkDirectoryRoot}", $"/p:RepoRoot={BuildVariables.RepoRoot}", $"/p:Configuration={project.Configuration}", $"/t:{target}", diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildResult.cs b/src/Shared/MSBuild.Testing/MSBuildResult.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildResult.cs rename to src/Shared/MSBuild.Testing/MSBuildResult.cs diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs b/src/Shared/MSBuild.Testing/ProjectDirectory.cs similarity index 97% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs rename to src/Shared/MSBuild.Testing/ProjectDirectory.cs index d0d0e571f4..0f669c31fc 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs +++ b/src/Shared/MSBuild.Testing/ProjectDirectory.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -49,12 +49,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } var repositoryRoot = BuildVariables.RepoRoot; - var solutionRoot = Path.Combine(repositoryRoot, "src", "Razor"); var binariesRoot = Path.GetDirectoryName(typeof(ProjectDirectory).Assembly.Location); + var testAppsRoot = BuildVariables.TestAppsRoot; foreach (var project in new string[] { originalProjectName, }.Concat(additionalProjects)) { - var testAppsRoot = Path.Combine(solutionRoot, "test", "testassets"); var projectRoot = Path.Combine(testAppsRoot, project); if (!Directory.Exists(projectRoot)) { @@ -146,6 +145,11 @@ $@" .ForEach(file => { var source = Path.Combine(testAppsRoot, file); + if (!File.Exists(source)) + { + return; + } + var destination = Path.Combine(projectDestination, file); File.Copy(source, destination); });