diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 825685a528..dd12efa35e 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -64,8 +64,6 @@ - - diff --git a/src/Components/Components.sln b/src/Components/Components.sln index 156f3870d8..722c3a4ea1 100644 --- a/src/Components/Components.sln +++ b/src/Components/Components.sln @@ -15,24 +15,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Tests", "WebAssembly\WebAssembly\test\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj", "{958AD6D2-174B-4B5B-BEFC-FA64B5159334}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Build", "WebAssembly\Build\src\Microsoft.AspNetCore.Components.WebAssembly.Build.csproj", "{E8AD67A4-77D3-4B85-AE19-4711388B62B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Build.Tests", "WebAssembly\Build\test\Microsoft.AspNetCore.Components.WebAssembly.Build.Tests.csproj", "{E38FDBB0-08C1-444E-A449-69C8A59D721B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.DevServer", "WebAssembly\DevServer\src\Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj", "{A6C8050D-7C18-4585-ADCF-833AC1765847}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Server", "WebAssembly\Server\src\Microsoft.AspNetCore.Components.WebAssembly.Server.csproj", "{A4859630-F9F7-4F5C-9FF3-6C013D7C58FA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{A7ABAC29-F73F-456D-AE54-46842CFC2E10}" + ProjectSection(SolutionItems) = preProject + WebAssembly\testassets\Wasm.Authentication.Server\Wasm.Authentication.Server.csproj = WebAssembly\testassets\Wasm.Authentication.Server\Wasm.Authentication.Server.csproj + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Client", "WebAssembly\testassets\HostedInAspNet.Client\HostedInAspNet.Client.csproj", "{FD37F740-A654-4117-BFB6-9112CE4C1D3B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Server", "WebAssembly\testassets\HostedInAspNet.Server\HostedInAspNet.Server.csproj", "{C1E2C117-BE47-4E29-94B3-753262D97A5C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "WebAssembly\testassets\MonoSanity\MonoSanity.csproj", "{F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanityClient", "WebAssembly\testassets\MonoSanityClient\MonoSanityClient.csproj", "{1C4BF2D3-44A8-4A71-B031-15B983663CB0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneApp", "WebAssembly\testassets\StandaloneApp\StandaloneApp.csproj", "{C0FFB29E-4696-4875-9039-E5FA1AC5A42A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{A27FF193-195B-4474-8E6C-840B2E339373}" @@ -244,16 +239,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Performance.TestApp", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebAssembly", "WebAssembly", "{346EC9B8-BF36-4A5E-A1A3-77879931713A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{1FA95650-E56E-470A-82A3-BC6572E4D6CD}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Server.Tests", "WebAssembly\Server\test\Microsoft.AspNetCore.Components.WebAssembly.Server.Tests.csproj", "{3D0ED658-9DAC-4066-A587-795321FA1C98}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{42E3C95D-A41E-4E14-96FD-AAE8F340FD7E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Authentication.WebAssembly.Msal", "WebAssembly\Authentication.Msal\src\Microsoft.Authentication.WebAssembly.Msal.csproj", "{4B4E4247-7BBF-444E-9737-407D34821D70}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression", "WebAssembly\Compression\src\Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression.csproj", "{1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.DebugProxy", "WebAssembly\DebugProxy\src\Microsoft.AspNetCore.Components.WebAssembly.DebugProxy.csproj", "{B118AE2F-8D1D-413F-BC5D-060DF7CB707D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DevServer", "DevServer", "{C6B58D53-04E2-4D65-B445-B510A3CB7569}" @@ -268,6 +259,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Authentication", "Authentic EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.HttpHandler", "WebAssembly\WebAssemblyHttpHandler\src\Microsoft.AspNetCore.Components.WebAssembly.HttpHandler.csproj", "{031AD67E-DDDE-4A20-874A-8D0A791C6F4C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Authentication.Client", "WebAssembly\testassets\Wasm.Authentication.Client\Wasm.Authentication.Client.csproj", "{5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Authentication.Shared", "WebAssembly\testassets\Wasm.Authentication.Shared\Wasm.Authentication.Shared.csproj", "{630D5388-7A2F-42DD-9154-1F62A18CBB69}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop", "..\JSInterop\Microsoft.JSInterop\src\Microsoft.JSInterop.csproj", "{A062ACCE-AB0D-4569-9548-4BC0D9A9174B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -326,30 +323,6 @@ Global {958AD6D2-174B-4B5B-BEFC-FA64B5159334}.Release|x64.Build.0 = Release|Any CPU {958AD6D2-174B-4B5B-BEFC-FA64B5159334}.Release|x86.ActiveCfg = Release|Any CPU {958AD6D2-174B-4B5B-BEFC-FA64B5159334}.Release|x86.Build.0 = Release|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Debug|x64.ActiveCfg = Debug|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Debug|x64.Build.0 = Debug|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Debug|x86.ActiveCfg = Debug|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Debug|x86.Build.0 = Debug|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Release|Any CPU.Build.0 = Release|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Release|x64.ActiveCfg = Release|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Release|x64.Build.0 = Release|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Release|x86.ActiveCfg = Release|Any CPU - {E8AD67A4-77D3-4B85-AE19-4711388B62B1}.Release|x86.Build.0 = Release|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Debug|x64.ActiveCfg = Debug|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Debug|x64.Build.0 = Debug|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Debug|x86.ActiveCfg = Debug|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Debug|x86.Build.0 = Debug|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Release|Any CPU.Build.0 = Release|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Release|x64.ActiveCfg = Release|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Release|x64.Build.0 = Release|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Release|x86.ActiveCfg = Release|Any CPU - {E38FDBB0-08C1-444E-A449-69C8A59D721B}.Release|x86.Build.0 = Release|Any CPU {A6C8050D-7C18-4585-ADCF-833AC1765847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A6C8050D-7C18-4585-ADCF-833AC1765847}.Debug|Any CPU.Build.0 = Debug|Any CPU {A6C8050D-7C18-4585-ADCF-833AC1765847}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -398,30 +371,6 @@ Global {C1E2C117-BE47-4E29-94B3-753262D97A5C}.Release|x64.Build.0 = Release|Any CPU {C1E2C117-BE47-4E29-94B3-753262D97A5C}.Release|x86.ActiveCfg = Release|Any CPU {C1E2C117-BE47-4E29-94B3-753262D97A5C}.Release|x86.Build.0 = Release|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Debug|x64.ActiveCfg = Debug|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Debug|x64.Build.0 = Debug|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Debug|x86.ActiveCfg = Debug|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Debug|x86.Build.0 = Debug|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Release|Any CPU.Build.0 = Release|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Release|x64.ActiveCfg = Release|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Release|x64.Build.0 = Release|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Release|x86.ActiveCfg = Release|Any CPU - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}.Release|x86.Build.0 = Release|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Debug|x64.ActiveCfg = Debug|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Debug|x64.Build.0 = Debug|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Debug|x86.ActiveCfg = Debug|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Debug|x86.Build.0 = Debug|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Release|Any CPU.Build.0 = Release|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Release|x64.ActiveCfg = Release|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Release|x64.Build.0 = Release|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Release|x86.ActiveCfg = Release|Any CPU - {1C4BF2D3-44A8-4A71-B031-15B983663CB0}.Release|x86.Build.0 = Release|Any CPU {C0FFB29E-4696-4875-9039-E5FA1AC5A42A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C0FFB29E-4696-4875-9039-E5FA1AC5A42A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C0FFB29E-4696-4875-9039-E5FA1AC5A42A}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1526,18 +1475,6 @@ Global {4B4E4247-7BBF-444E-9737-407D34821D70}.Release|x64.Build.0 = Release|Any CPU {4B4E4247-7BBF-444E-9737-407D34821D70}.Release|x86.ActiveCfg = Release|Any CPU {4B4E4247-7BBF-444E-9737-407D34821D70}.Release|x86.Build.0 = Release|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Debug|x64.ActiveCfg = Debug|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Debug|x64.Build.0 = Debug|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Debug|x86.ActiveCfg = Debug|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Debug|x86.Build.0 = Debug|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Release|Any CPU.Build.0 = Release|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Release|x64.ActiveCfg = Release|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Release|x64.Build.0 = Release|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Release|x86.ActiveCfg = Release|Any CPU - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE}.Release|x86.Build.0 = Release|Any CPU {B118AE2F-8D1D-413F-BC5D-060DF7CB707D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B118AE2F-8D1D-413F-BC5D-060DF7CB707D}.Debug|Any CPU.Build.0 = Debug|Any CPU {B118AE2F-8D1D-413F-BC5D-060DF7CB707D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1598,6 +1535,42 @@ Global {031AD67E-DDDE-4A20-874A-8D0A791C6F4C}.Release|x64.Build.0 = Release|Any CPU {031AD67E-DDDE-4A20-874A-8D0A791C6F4C}.Release|x86.ActiveCfg = Release|Any CPU {031AD67E-DDDE-4A20-874A-8D0A791C6F4C}.Release|x86.Build.0 = Release|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Debug|x64.ActiveCfg = Debug|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Debug|x64.Build.0 = Debug|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Debug|x86.ActiveCfg = Debug|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Debug|x86.Build.0 = Debug|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Release|Any CPU.Build.0 = Release|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Release|x64.ActiveCfg = Release|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Release|x64.Build.0 = Release|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Release|x86.ActiveCfg = Release|Any CPU + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0}.Release|x86.Build.0 = Release|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Debug|x64.ActiveCfg = Debug|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Debug|x64.Build.0 = Debug|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Debug|x86.ActiveCfg = Debug|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Debug|x86.Build.0 = Debug|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Release|Any CPU.Build.0 = Release|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Release|x64.ActiveCfg = Release|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Release|x64.Build.0 = Release|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Release|x86.ActiveCfg = Release|Any CPU + {630D5388-7A2F-42DD-9154-1F62A18CBB69}.Release|x86.Build.0 = Release|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Debug|x64.ActiveCfg = Debug|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Debug|x64.Build.0 = Debug|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Debug|x86.ActiveCfg = Debug|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Debug|x86.Build.0 = Debug|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Release|Any CPU.Build.0 = Release|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Release|x64.ActiveCfg = Release|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Release|x64.Build.0 = Release|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Release|x86.ActiveCfg = Release|Any CPU + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1607,15 +1580,11 @@ Global {F000C49D-3857-42A4-918D-DA4C08691FE2} = {E059A46B-56E3-41E2-83F4-B5D180056F3B} {641922CD-E6F5-41E7-A085-EE07C2A7328D} = {346EC9B8-BF36-4A5E-A1A3-77879931713A} {958AD6D2-174B-4B5B-BEFC-FA64B5159334} = {346EC9B8-BF36-4A5E-A1A3-77879931713A} - {E8AD67A4-77D3-4B85-AE19-4711388B62B1} = {1FA95650-E56E-470A-82A3-BC6572E4D6CD} - {E38FDBB0-08C1-444E-A449-69C8A59D721B} = {1FA95650-E56E-470A-82A3-BC6572E4D6CD} {A6C8050D-7C18-4585-ADCF-833AC1765847} = {C6B58D53-04E2-4D65-B445-B510A3CB7569} {A4859630-F9F7-4F5C-9FF3-6C013D7C58FA} = {42E3C95D-A41E-4E14-96FD-AAE8F340FD7E} {A7ABAC29-F73F-456D-AE54-46842CFC2E10} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} {FD37F740-A654-4117-BFB6-9112CE4C1D3B} = {A7ABAC29-F73F-456D-AE54-46842CFC2E10} {C1E2C117-BE47-4E29-94B3-753262D97A5C} = {A7ABAC29-F73F-456D-AE54-46842CFC2E10} - {F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E} = {A7ABAC29-F73F-456D-AE54-46842CFC2E10} - {1C4BF2D3-44A8-4A71-B031-15B983663CB0} = {A7ABAC29-F73F-456D-AE54-46842CFC2E10} {C0FFB29E-4696-4875-9039-E5FA1AC5A42A} = {A7ABAC29-F73F-456D-AE54-46842CFC2E10} {3B1A56F8-B3E0-4F33-A717-50BDD4FBE12E} = {A27FF193-195B-4474-8E6C-840B2E339373} {35A8AE1D-ED82-485E-A8E6-A357B3CB31B3} = {3D9B9B2C-E379-41BD-83D4-2E099FBDA107} @@ -1709,11 +1678,9 @@ Global {CA9948CA-B3FA-4C2E-A726-5E47BAD19457} = {F65EFF0F-ACF3-46BD-9A8F-CDA94AF1885A} {97EA0A7D-FE5E-47D1-ADDC-4BFD702F55AB} = {F65EFF0F-ACF3-46BD-9A8F-CDA94AF1885A} {346EC9B8-BF36-4A5E-A1A3-77879931713A} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} - {1FA95650-E56E-470A-82A3-BC6572E4D6CD} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} {3D0ED658-9DAC-4066-A587-795321FA1C98} = {42E3C95D-A41E-4E14-96FD-AAE8F340FD7E} {42E3C95D-A41E-4E14-96FD-AAE8F340FD7E} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} {4B4E4247-7BBF-444E-9737-407D34821D70} = {81250121-9B43-40B1-BF11-CE4458F2676C} - {1A4C96E8-3FAF-48FB-9F3C-068FAAAB3FEE} = {1FA95650-E56E-470A-82A3-BC6572E4D6CD} {B118AE2F-8D1D-413F-BC5D-060DF7CB707D} = {C6B58D53-04E2-4D65-B445-B510A3CB7569} {C6B58D53-04E2-4D65-B445-B510A3CB7569} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} {8FDD9F2E-B940-4A5F-83FD-5486D0853D76} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} @@ -1721,6 +1688,9 @@ Global {6B0D6C08-FC30-4822-9464-4D24FF4CDC17} = {81250121-9B43-40B1-BF11-CE4458F2676C} {81250121-9B43-40B1-BF11-CE4458F2676C} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} {031AD67E-DDDE-4A20-874A-8D0A791C6F4C} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} + {5F12F7B9-70BE-48F6-922A-3A1E87EE6BF0} = {A7ABAC29-F73F-456D-AE54-46842CFC2E10} + {630D5388-7A2F-42DD-9154-1F62A18CBB69} = {A7ABAC29-F73F-456D-AE54-46842CFC2E10} + {A062ACCE-AB0D-4569-9548-4BC0D9A9174B} = {2FC10057-7A0A-4E34-8302-879925BC0102} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CC3C47E1-AD1A-4619-9CD3-E08A0148E5CE} diff --git a/src/Components/ComponentsNoDeps.slnf b/src/Components/ComponentsNoDeps.slnf index 2a45ec68c9..0484ef907c 100644 --- a/src/Components/ComponentsNoDeps.slnf +++ b/src/Components/ComponentsNoDeps.slnf @@ -17,9 +17,6 @@ "Server\\src\\Microsoft.AspNetCore.Components.Server.csproj", "Server\\test\\Microsoft.AspNetCore.Components.Server.Tests.csproj", "WebAssembly\\Authentication.Msal\\src\\Microsoft.Authentication.WebAssembly.Msal.csproj", - "WebAssembly\\Build\\src\\Microsoft.AspNetCore.Components.WebAssembly.Build.csproj", - "WebAssembly\\Build\\test\\Microsoft.AspNetCore.Components.WebAssembly.Build.Tests.csproj", - "WebAssembly\\Compression\\src\\Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression.csproj", "WebAssembly\\DebugProxy\\src\\Microsoft.AspNetCore.Components.WebAssembly.DebugProxy.csproj", "WebAssembly\\DevServer\\src\\Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj", "WebAssembly\\JSInterop\\src\\Microsoft.JSInterop.WebAssembly.csproj", @@ -32,8 +29,6 @@ "WebAssembly\\WebAssembly\\test\\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj", "WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj", "WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj", - "WebAssembly\\testassets\\MonoSanityClient\\MonoSanityClient.csproj", - "WebAssembly\\testassets\\MonoSanity\\MonoSanity.csproj", "WebAssembly\\testassets\\StandaloneApp\\StandaloneApp.csproj", "Web\\src\\Microsoft.AspNetCore.Components.Web.csproj", "Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj", diff --git a/src/Components/Directory.Build.targets b/src/Components/Directory.Build.targets index 9a5acfd3e6..1ecdb38eaf 100644 --- a/src/Components/Directory.Build.targets +++ b/src/Components/Directory.Build.targets @@ -1,9 +1,18 @@  + + $(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 + dotnet + exec "$(_BlazorDevServerPath)" serve --applicationpath "$(TargetPath)" $(AdditionalRunArguments) + + - - diff --git a/src/Components/Web.JS/dist/Release/blazor.webassembly.js b/src/Components/Web.JS/dist/Release/blazor.webassembly.js index a04cf089d7..59c0e5f467 100644 --- a/src/Components/Web.JS/dist/Release/blazor.webassembly.js +++ b/src/Components/Web.JS/dist/Release/blazor.webassembly.js @@ -1 +1 @@ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=43)}([,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e){window.DotNet=e;var t=[],n={},r={},o=1,a=null;function i(e){t.push(e)}function u(e,t,n,r){var o=c();if(o.invokeDotNetFromJS){var a=JSON.stringify(r,m),i=o.invokeDotNetFromJS(e,t,n,a);return i?f(i):null}throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.")}function s(e,t,r,a){if(e&&r)throw new Error("For instance method calls, assemblyName should be null. Received '"+e+"'.");var i=o++,u=new Promise((function(e,t){n[i]={resolve:e,reject:t}}));try{var s=JSON.stringify(a,m);c().beginInvokeDotNetFromJS(i,e,t,r,s)}catch(e){l(i,!1,e)}return u}function c(){if(null!==a)return a;throw new Error("No .NET call dispatcher has been set.")}function l(e,t,r){if(!n.hasOwnProperty(e))throw new Error("There is no pending async call with ID "+e+".");var o=n[e];delete n[e],t?o.resolve(r):o.reject(r)}function f(e){return e?JSON.parse(e,(function(e,n){return t.reduce((function(t,n){return n(e,t)}),n)})):null}function d(e){return e instanceof Error?e.message+"\n"+e.stack:e?e.toString():"null"}function p(e){if(r.hasOwnProperty(e))return r[e];var t,n=window,o="window";if(e.split(".").forEach((function(e){if(!(e in n))throw new Error("Could not find '"+e+"' in '"+o+"'.");t=n,n=n[e],o+="."+e})),n instanceof Function)return n=n.bind(t),r[e]=n,n;throw new Error("The value '"+o+"' is not a function.")}e.attachDispatcher=function(e){a=e},e.attachReviver=i,e.invokeMethod=function(e,t){for(var n=[],r=2;r0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return r in e||(e[r]=[]),e}function u(e,t,n){var a=e;if(e instanceof Comment&&(c(a)&&c(a).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(s(a))throw new Error("Not implemented: moving existing logical children");var i=c(t);if(n0;)e(r,0);var a=r;a.parentNode.removeChild(a)},t.getLogicalParent=s,t.getLogicalSiblingEnd=function(e){return e[a]||null},t.getLogicalChild=function(e,t){return c(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===l(e).namespaceURI},t.getLogicalChildrenArray=c,t.permuteLogicalChildren=function(e,t){var n=c(e);t.forEach((function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=s(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)})),t.forEach((function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):d(r,e)})),t.forEach((function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,a=r;a;){var i=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=i}n.removeChild(t)})),t.forEach((function(e){n[e.toSiblingIndex]=e.moveRangeStart}))},t.getClosestDomElement=l},,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.setPlatform=function(e){return t.platform=e,t.platform}},function(e,t,n){"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.dispatchEvent=function(e,t){if(!r)throw new Error("eventDispatcher not initialized. Call 'setEventDispatcher' to configure it.");return r(e,t)},t.setEventDispatcher=function(e){r=e}},,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(7),o=n(4);window.Blazor={navigateTo:r.navigateTo,_internal:{attachRootComponentToElement:o.attachRootComponentToElement,navigationManager:r.internalFunctions}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(25),o=n(26),a=n(12),i=n(29),u=n(18),s=n(7),c=document.createElement("template"),l=document.createElementNS("http://www.w3.org/2000/svg","g"),f={submit:!0},d={},p=function(){function e(e){var t=this;this.childComponentLocations={},this.browserRendererId=e,this.eventDelegator=new o.EventDelegator((function(e,n,r,o){!function(e,t,n,r,o){f[e.type]&&e.preventDefault();var a={browserRendererId:t,eventHandlerId:n,eventArgsType:r.type,eventFieldInfo:o};u.dispatchEvent(a,r.data)}(e,t.browserRendererId,n,r,o)})),s.attachToEventDelegator(this.eventDelegator)}return e.prototype.attachRootComponentToLogicalElement=function(e,t){this.attachComponentToElement(e,t),d[e]=t},e.prototype.updateComponent=function(e,t,n,r){var o=this.childComponentLocations[t];if(!o)throw new Error("No element is currently associated with component "+t);var i=d[t];if(i){var u=a.getLogicalSiblingEnd(i);delete d[t],u?function(e,t){var n=a.getLogicalParent(e);if(!n)throw new Error("Can't clear between nodes. The start node does not have a logical parent.");for(var r=a.getLogicalChildrenArray(n),o=r.indexOf(e)+1,i=r.indexOf(t),u=o;u<=i;u++)a.removeLogicalChild(n,o);e.textContent="!"}(i,u):function(e){var t;for(;t=e.firstChild;)e.removeChild(t)}(i)}var s=a.getClosestDomElement(o).ownerDocument,c=s&&s.activeElement;this.applyEdits(e,t,o,0,n,r),c instanceof HTMLElement&&s&&s.activeElement!==c&&c.focus()},e.prototype.disposeComponent=function(e){delete this.childComponentLocations[e]},e.prototype.disposeEventHandler=function(e){this.eventDelegator.removeListener(e)},e.prototype.attachComponentToElement=function(e,t){this.childComponentLocations[e]=t},e.prototype.applyEdits=function(e,t,n,o,i,u){for(var s,c=0,l=o,f=e.arrayBuilderSegmentReader,d=e.editReader,p=e.frameReader,h=f.values(i),m=f.offset(i),v=m+f.count(i),y=m;y0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]>>0)}t.readInt32LE=function(e,t){return e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24},t.readUint32LE=a,t.readUint64LE=function(e,t){var n=a(e,t+4);if(n>o)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*r+a(e,t)},t.readLEB128=function(e,t){for(var n=0,r=0,o=0;o<4;o++){var a=e[t+o];if(n|=(127&a)<65535&&(c-=65536,r.push(c>>>10&1023|55296),c=56320|1023&c),r.push(c)}r.length>1024&&(o.push(String.fromCharCode.apply(null,r)),r.length=0)}return o.push(String.fromCharCode.apply(null,r)),o.join("")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.shouldAutoStart=function(){return!(!document||!document.currentScript||"false"===document.currentScript.getAttribute("autostart"))}},,,,,,,,,,function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,a){function i(e){try{s(r.next(e))}catch(e){a(e)}}function u(e){try{s(r.throw(e))}catch(e){a(e)}}function s(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,u)}s((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(a){return function(u){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0)&&!(r=a.next()).done;)i.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=a.return)&&n.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var i=n(3);n(22);var u=n(17),s=n(44),c=n(4),l=n(47),f=n(33),d=n(18),p=n(48),h=n(49),m=n(50),v=!1;function y(e){return r(this,void 0,void 0,(function(){var t,n,f,y,b,g,w,E=this;return o(this,(function(_){switch(_.label){case 0:if(v)throw new Error("Blazor has already started.");return v=!0,d.setEventDispatcher((function(e,t){return i.DotNet.invokeMethodAsync("Microsoft.AspNetCore.Components.WebAssembly","DispatchEvent",e,JSON.stringify(t))})),t=u.setPlatform(s.monoPlatform),window.Blazor.platform=t,window.Blazor._internal.renderBatch=function(e,t){c.renderBatch(e,new l.SharedMemoryRenderBatch(t))},n=window.Blazor._internal.navigationManager.getBaseURI,f=window.Blazor._internal.navigationManager.getLocationHref,window.Blazor._internal.navigationManager.getUnmarshalledBaseURI=function(){return BINDING.js_string_to_mono_string(n())},window.Blazor._internal.navigationManager.getUnmarshalledLocationHref=function(){return BINDING.js_string_to_mono_string(f())},window.Blazor._internal.navigationManager.listenForNavigationEvents((function(e,t){return r(E,void 0,void 0,(function(){return o(this,(function(n){switch(n.label){case 0:return[4,i.DotNet.invokeMethodAsync("Microsoft.AspNetCore.Components.WebAssembly","NotifyLocationChanged",e,t)];case 1:return n.sent(),[2]}}))}))})),[4,m.BootConfigResult.initAsync()];case 1:return y=_.sent(),[4,Promise.all([p.WebAssemblyResourceLoader.initAsync(y.bootConfig,e||{}),h.WebAssemblyConfigLoader.initAsync(y)])];case 2:b=a.apply(void 0,[_.sent(),1]),g=b[0],_.label=3;case 3:return _.trys.push([3,5,,6]),[4,t.start(g)];case 4:return _.sent(),[3,6];case 5:throw w=_.sent(),new Error("Failed to start platform. Reason: "+w);case 6:return t.callEntryPoint(g.bootConfig.entryAssembly),[2]}}))}))}window.Blazor.start=y,f.shouldAutoStart()&&y().catch((function(e){"undefined"!=typeof Module&&Module.printErr?Module.printErr(e):console.error(e)}))},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,a){function i(e){try{s(r.next(e))}catch(e){a(e)}}function u(e){try{s(r.throw(e))}catch(e){a(e)}}function s(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,u)}s((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(a){return function(u){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]>2]}t.monoPlatform={start:function(e){return new Promise((function(t,n){var l,f;u.attachDebuggerHotkey(e),window.Browser={init:function(){}},l=function(){window.Module=function(e,t,n){var l=this,f=e.bootConfig.resources,d=window.Module||{},h=["DEBUGGING ENABLED"];d.print=function(e){return h.indexOf(e)<0&&console.log(e)},d.printErr=function(e){console.error(e),s.showErrorNotification()},d.preRun=d.preRun||[],d.postRun=d.postRun||[],d.preloadPlugins=[];var y,b=e.loadResources(f.assembly,(function(e){return"_framework/_bin/"+e}),"assembly"),g=e.loadResources(f.pdb||{},(function(e){return"_framework/_bin/"+e}),"pdb"),w=e.loadResource("dotnet.wasm","_framework/wasm/dotnet.wasm",e.bootConfig.resources.runtime["dotnet.wasm"],"dotnetwasm");return e.bootConfig.resources.runtime.hasOwnProperty("dotnet.timezones.dat")&&(y=e.loadResource("dotnet.timezones.dat","_framework/wasm/dotnet.timezones.dat",e.bootConfig.resources.runtime["dotnet.timezones.dat"],"timezonedata")),d.instantiateWasm=function(e,t){return r(l,void 0,void 0,(function(){var n,r;return o(this,(function(o){switch(o.label){case 0:return o.trys.push([0,3,,4]),[4,w];case 1:return[4,v(o.sent(),e)];case 2:return n=o.sent(),[3,4];case 3:throw r=o.sent(),d.printErr(r),r;case 4:return t(n),[2]}}))})),[]},d.preRun.push((function(){a=cwrap("mono_wasm_add_assembly",null,["string","number","number"]),cwrap("mono_wasm_string_get_utf8","number",["number"]),MONO.loaded_files=[],y&&function(e){r(this,void 0,void 0,(function(){var t,n;return o(this,(function(r){switch(r.label){case 0:return t="blazor:timezonedata",addRunDependency(t),[4,e.response];case 1:return[4,r.sent().arrayBuffer()];case 2:return n=r.sent(),c.loadTimezoneData(n),removeRunDependency(t),[2]}}))}))}(y),b.forEach((function(e){return E(e,function(e,t){var n=e.lastIndexOf(".");if(n<0)throw new Error("No extension to replace in '"+e+"'");return e.substr(0,n)+t}(e.name,".dll"))})),g.forEach((function(e){return E(e,e.name)})),window.Blazor._internal.dotNetCriticalError=function(e){d.printErr(BINDING.conv_string(e)||"(null)")},window.Blazor._internal.getSatelliteAssemblies=function(t){var n=BINDING.mono_array_to_js_array(t),a=e.bootConfig.resources.satelliteResources;if(a){var i=Promise.all(n.filter((function(e){return a.hasOwnProperty(e)})).map((function(t){return e.loadResources(a[t],(function(e){return"_framework/_bin/"+e}),"assembly")})).reduce((function(e,t){return e.concat(t)}),new Array).map((function(e){return r(l,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return[4,e.response];case 1:return[2,t.sent().arrayBuffer()]}}))}))})));return BINDING.js_to_mono_obj(i.then((function(e){return e.length&&(window.Blazor._internal.readSatelliteAssemblies=function(){for(var t=BINDING.mono_obj_array_new(e.length),n=0;n>1];var n},readInt32Field:function(e,t){return d(e+(t||0))},readUint64Field:function(e,t){return function(e){var t=e>>2,n=Module.HEAPU32[t+1];if(n>f)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*l+Module.HEAPU32[t]}(e+(t||0))},readFloatField:function(e,t){return n=e+(t||0),Module.HEAPF32[n>>2];var n},readObjectField:function(e,t){return d(e+(t||0))},readStringField:function(e,t,n){var r=d(e+(t||0));if(0===r)return null;if(n){var o=BINDING.unbox_mono_obj(r);return"boolean"==typeof o?o?"":null:o}return BINDING.conv_string(r)},readStructField:function(e,t){return e+(t||0)}};var p=document.createElement("a");function h(e){return e+12}function m(e,t,n){var r="["+e+"] "+t+":"+n;return BINDING.bind_static_method(r)}function v(e,t){return r(this,void 0,void 0,(function(){var n,r;return o(this,(function(o){switch(o.label){case 0:if("function"!=typeof WebAssembly.instantiateStreaming)return[3,4];o.label=1;case 1:return o.trys.push([1,3,,4]),[4,WebAssembly.instantiateStreaming(e.response,t)];case 2:return[2,o.sent().instance];case 3:return n=o.sent(),console.info("Streaming compilation failed. Falling back to ArrayBuffer instantiation. ",n),[3,4];case 4:return[4,e.response.then((function(e){return e.arrayBuffer()}))];case 5:return r=o.sent(),[4,WebAssembly.instantiate(r,t)];case 6:return[2,o.sent().instance]}}))}))}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=window.chrome&&navigator.userAgent.indexOf("Edge")<0,o=!1;function a(){return o&&r}t.hasDebuggingEnabled=a,t.attachDebuggerHotkey=function(e){o=!!e.bootConfig.resources.pdb;var t=navigator.platform.match(/^Mac/i)?"Cmd":"Alt";a()&&console.info("Debugging hotkey: Shift+"+t+"+D (when application has focus)"),document.addEventListener("keydown",(function(e){var t;e.shiftKey&&(e.metaKey||e.altKey)&&"KeyD"===e.code&&(o?r?((t=document.createElement("a")).href="_framework/debug?url="+encodeURIComponent(location.href),t.target="_blank",t.rel="noopener noreferrer",t.click()):console.error("Currently, only Microsoft Edge (80+), or Google Chrome, are supported for debugging."):console.error("Cannot start debugging, because the application was not compiled with debugging enabled."))}))}},function(e,t,n){"use strict";var r=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,a=n.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(r=a.next()).done;)i.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=a.return)&&n.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var a=n(31),i=n(32);t.loadTimezoneData=function(e){var t,n,u=new Uint8Array(e),s=a.readInt32LE(u,0);u=u.slice(4);var c=i.decodeUtf8(u.slice(0,s)),l=JSON.parse(c);u=u.slice(s),Module.FS_createPath("/","zoneinfo",!0,!0),new Set(l.map((function(e){return e[0].split("/")[0]}))).forEach((function(e){return Module.FS_createPath("/zoneinfo",e,!0,!0)}));try{for(var f=r(l),d=f.next();!d.done;d=f.next()){var p=o(d.value,2),h=p[0],m=p[1],v=u.slice(0,m);Module.FS_createDataFile("/zoneinfo/"+h,null,v,!0,!0,!0),u=u.slice(m)}}catch(e){t={error:e}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(t)throw t.error}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(17),o=function(){function e(e){this.batchAddress=e,this.arrayRangeReader=a,this.arrayBuilderSegmentReader=i,this.diffReader=u,this.editReader=s,this.frameReader=c}return e.prototype.updatedComponents=function(){return r.platform.readStructField(this.batchAddress,0)},e.prototype.referenceFrames=function(){return r.platform.readStructField(this.batchAddress,a.structLength)},e.prototype.disposedComponentIds=function(){return r.platform.readStructField(this.batchAddress,2*a.structLength)},e.prototype.disposedEventHandlerIds=function(){return r.platform.readStructField(this.batchAddress,3*a.structLength)},e.prototype.updatedComponentsEntry=function(e,t){return l(e,t,u.structLength)},e.prototype.referenceFramesEntry=function(e,t){return l(e,t,c.structLength)},e.prototype.disposedComponentIdsEntry=function(e,t){var n=l(e,t,4);return r.platform.readInt32Field(n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=l(e,t,8);return r.platform.readUint64Field(n)},e}();t.SharedMemoryRenderBatch=o;var a={structLength:8,values:function(e){return r.platform.readObjectField(e,0)},count:function(e){return r.platform.readInt32Field(e,4)}},i={structLength:12,values:function(e){var t=r.platform.readObjectField(e,0),n=r.platform.getObjectFieldsBaseAddress(t);return r.platform.readObjectField(n,0)},offset:function(e){return r.platform.readInt32Field(e,4)},count:function(e){return r.platform.readInt32Field(e,8)}},u={structLength:4+i.structLength,componentId:function(e){return r.platform.readInt32Field(e,0)},edits:function(e){return r.platform.readStructField(e,4)},editsEntry:function(e,t){return l(e,t,s.structLength)}},s={structLength:20,editType:function(e){return r.platform.readInt32Field(e,0)},siblingIndex:function(e){return r.platform.readInt32Field(e,4)},newTreeIndex:function(e){return r.platform.readInt32Field(e,8)},moveToSiblingIndex:function(e){return r.platform.readInt32Field(e,8)},removedAttributeName:function(e){return r.platform.readStringField(e,16)}},c={structLength:36,frameType:function(e){return r.platform.readInt16Field(e,4)},subtreeLength:function(e){return r.platform.readInt32Field(e,8)},elementReferenceCaptureId:function(e){return r.platform.readStringField(e,16)},componentId:function(e){return r.platform.readInt32Field(e,12)},elementName:function(e){return r.platform.readStringField(e,16)},textContent:function(e){return r.platform.readStringField(e,16)},markupContent:function(e){return r.platform.readStringField(e,16)},attributeName:function(e){return r.platform.readStringField(e,16)},attributeValue:function(e){return r.platform.readStringField(e,24,!0)},attributeEventHandlerId:function(e){return r.platform.readUint64Field(e,8)}};function l(e,t,n){return r.platform.getArrayEntryPtr(e,t,n)}},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,a){function i(e){try{s(r.next(e))}catch(e){a(e)}}function u(e){try{s(r.throw(e))}catch(e){a(e)}}function s(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,u)}s((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(a){return function(u){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return r in e||(e[r]=[]),e}function u(e,t,n){var a=e;if(e instanceof Comment&&(c(a)&&c(a).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(s(a))throw new Error("Not implemented: moving existing logical children");var i=c(t);if(n0;)e(r,0);var a=r;a.parentNode.removeChild(a)},t.getLogicalParent=s,t.getLogicalSiblingEnd=function(e){return e[a]||null},t.getLogicalChild=function(e,t){return c(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===l(e).namespaceURI},t.getLogicalChildrenArray=c,t.permuteLogicalChildren=function(e,t){var n=c(e);t.forEach((function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=s(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)})),t.forEach((function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):d(r,e)})),t.forEach((function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,a=r;a;){var i=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=i}n.removeChild(t)})),t.forEach((function(e){n[e.toSiblingIndex]=e.moveRangeStart}))},t.getClosestDomElement=l},,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.setPlatform=function(e){return t.platform=e,t.platform}},function(e,t,n){"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.dispatchEvent=function(e,t){if(!r)throw new Error("eventDispatcher not initialized. Call 'setEventDispatcher' to configure it.");return r(e,t)},t.setEventDispatcher=function(e){r=e}},,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(7),o=n(4);window.Blazor={navigateTo:r.navigateTo,_internal:{attachRootComponentToElement:o.attachRootComponentToElement,navigationManager:r.internalFunctions}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(25),o=n(26),a=n(12),i=n(29),u=n(18),s=n(7),c=document.createElement("template"),l=document.createElementNS("http://www.w3.org/2000/svg","g"),f={submit:!0},d={},p=function(){function e(e){var t=this;this.childComponentLocations={},this.browserRendererId=e,this.eventDelegator=new o.EventDelegator((function(e,n,r,o){!function(e,t,n,r,o){f[e.type]&&e.preventDefault();var a={browserRendererId:t,eventHandlerId:n,eventArgsType:r.type,eventFieldInfo:o};u.dispatchEvent(a,r.data)}(e,t.browserRendererId,n,r,o)})),s.attachToEventDelegator(this.eventDelegator)}return e.prototype.attachRootComponentToLogicalElement=function(e,t){this.attachComponentToElement(e,t),d[e]=t},e.prototype.updateComponent=function(e,t,n,r){var o=this.childComponentLocations[t];if(!o)throw new Error("No element is currently associated with component "+t);var i=d[t];if(i){var u=a.getLogicalSiblingEnd(i);delete d[t],u?function(e,t){var n=a.getLogicalParent(e);if(!n)throw new Error("Can't clear between nodes. The start node does not have a logical parent.");for(var r=a.getLogicalChildrenArray(n),o=r.indexOf(e)+1,i=r.indexOf(t),u=o;u<=i;u++)a.removeLogicalChild(n,o);e.textContent="!"}(i,u):function(e){var t;for(;t=e.firstChild;)e.removeChild(t)}(i)}var s=a.getClosestDomElement(o).ownerDocument,c=s&&s.activeElement;this.applyEdits(e,t,o,0,n,r),c instanceof HTMLElement&&s&&s.activeElement!==c&&c.focus()},e.prototype.disposeComponent=function(e){delete this.childComponentLocations[e]},e.prototype.disposeEventHandler=function(e){this.eventDelegator.removeListener(e)},e.prototype.attachComponentToElement=function(e,t){this.childComponentLocations[e]=t},e.prototype.applyEdits=function(e,t,n,o,i,u){for(var s,c=0,l=o,f=e.arrayBuilderSegmentReader,d=e.editReader,p=e.frameReader,h=f.values(i),m=f.offset(i),v=m+f.count(i),y=m;y0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]>>0)}t.readInt32LE=function(e,t){return e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24},t.readUint32LE=a,t.readUint64LE=function(e,t){var n=a(e,t+4);if(n>o)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*r+a(e,t)},t.readLEB128=function(e,t){for(var n=0,r=0,o=0;o<4;o++){var a=e[t+o];if(n|=(127&a)<65535&&(c-=65536,r.push(c>>>10&1023|55296),c=56320|1023&c),r.push(c)}r.length>1024&&(o.push(String.fromCharCode.apply(null,r)),r.length=0)}return o.push(String.fromCharCode.apply(null,r)),o.join("")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.shouldAutoStart=function(){return!(!document||!document.currentScript||"false"===document.currentScript.getAttribute("autostart"))}},,,,,,,,,,function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,a){function i(e){try{s(r.next(e))}catch(e){a(e)}}function u(e){try{s(r.throw(e))}catch(e){a(e)}}function s(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,u)}s((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(a){return function(u){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0)&&!(r=a.next()).done;)i.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=a.return)&&n.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var i=n(3);n(22);var u=n(17),s=n(44),c=n(4),l=n(47),f=n(33),d=n(18),p=n(48),h=n(49),m=n(50),v=!1;function y(e){return r(this,void 0,void 0,(function(){var t,n,f,y,b,g,w,E=this;return o(this,(function(_){switch(_.label){case 0:if(v)throw new Error("Blazor has already started.");return v=!0,d.setEventDispatcher((function(e,t){return i.DotNet.invokeMethodAsync("Microsoft.AspNetCore.Components.WebAssembly","DispatchEvent",e,JSON.stringify(t))})),t=u.setPlatform(s.monoPlatform),window.Blazor.platform=t,window.Blazor._internal.renderBatch=function(e,t){c.renderBatch(e,new l.SharedMemoryRenderBatch(t))},n=window.Blazor._internal.navigationManager.getBaseURI,f=window.Blazor._internal.navigationManager.getLocationHref,window.Blazor._internal.navigationManager.getUnmarshalledBaseURI=function(){return BINDING.js_string_to_mono_string(n())},window.Blazor._internal.navigationManager.getUnmarshalledLocationHref=function(){return BINDING.js_string_to_mono_string(f())},window.Blazor._internal.navigationManager.listenForNavigationEvents((function(e,t){return r(E,void 0,void 0,(function(){return o(this,(function(n){switch(n.label){case 0:return[4,i.DotNet.invokeMethodAsync("Microsoft.AspNetCore.Components.WebAssembly","NotifyLocationChanged",e,t)];case 1:return n.sent(),[2]}}))}))})),[4,m.BootConfigResult.initAsync()];case 1:return y=_.sent(),[4,Promise.all([p.WebAssemblyResourceLoader.initAsync(y.bootConfig,e||{}),h.WebAssemblyConfigLoader.initAsync(y)])];case 2:b=a.apply(void 0,[_.sent(),1]),g=b[0],_.label=3;case 3:return _.trys.push([3,5,,6]),[4,t.start(g)];case 4:return _.sent(),[3,6];case 5:throw w=_.sent(),new Error("Failed to start platform. Reason: "+w);case 6:return t.callEntryPoint(g.bootConfig.entryAssembly),[2]}}))}))}window.Blazor.start=y,f.shouldAutoStart()&&y().catch((function(e){"undefined"!=typeof Module&&Module.printErr?Module.printErr(e):console.error(e)}))},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,a){function i(e){try{s(r.next(e))}catch(e){a(e)}}function u(e){try{s(r.throw(e))}catch(e){a(e)}}function s(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,u)}s((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(a){return function(u){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]>2]}t.monoPlatform={start:function(e){return new Promise((function(t,n){var l,f;u.attachDebuggerHotkey(e),window.Browser={init:function(){}},l=function(){window.Module=function(e,t,n){var l=this,f=e.bootConfig.resources,d=window.Module||{},h=["DEBUGGING ENABLED"];d.print=function(e){return h.indexOf(e)<0&&console.log(e)},d.printErr=function(e){console.error(e),s.showErrorNotification()},d.preRun=d.preRun||[],d.postRun=d.postRun||[],d.preloadPlugins=[];var y,b=e.loadResources(f.assembly,(function(e){return"_framework/"+e}),"assembly"),g=e.loadResources(f.pdb||{},(function(e){return"_framework/"+e}),"pdb"),w=e.loadResource("dotnet.wasm","_framework/dotnet.wasm",e.bootConfig.resources.runtime["dotnet.wasm"],"dotnetwasm");return e.bootConfig.resources.runtime.hasOwnProperty("dotnet.timezones.dat")&&(y=e.loadResource("dotnet.timezones.dat","_framework/dotnet.timezones.dat",e.bootConfig.resources.runtime["dotnet.timezones.dat"],"timezonedata")),d.instantiateWasm=function(e,t){return r(l,void 0,void 0,(function(){var n,r;return o(this,(function(o){switch(o.label){case 0:return o.trys.push([0,3,,4]),[4,w];case 1:return[4,v(o.sent(),e)];case 2:return n=o.sent(),[3,4];case 3:throw r=o.sent(),d.printErr(r),r;case 4:return t(n),[2]}}))})),[]},d.preRun.push((function(){a=cwrap("mono_wasm_add_assembly",null,["string","number","number"]),cwrap("mono_wasm_string_get_utf8","number",["number"]),MONO.loaded_files=[],y&&function(e){r(this,void 0,void 0,(function(){var t,n;return o(this,(function(r){switch(r.label){case 0:return t="blazor:timezonedata",addRunDependency(t),[4,e.response];case 1:return[4,r.sent().arrayBuffer()];case 2:return n=r.sent(),c.loadTimezoneData(n),removeRunDependency(t),[2]}}))}))}(y),b.forEach((function(e){return E(e,function(e,t){var n=e.lastIndexOf(".");if(n<0)throw new Error("No extension to replace in '"+e+"'");return e.substr(0,n)+t}(e.name,".dll"))})),g.forEach((function(e){return E(e,e.name)})),window.Blazor._internal.dotNetCriticalError=function(e){d.printErr(BINDING.conv_string(e)||"(null)")},window.Blazor._internal.getSatelliteAssemblies=function(t){var n=BINDING.mono_array_to_js_array(t),a=e.bootConfig.resources.satelliteResources;if(a){var i=Promise.all(n.filter((function(e){return a.hasOwnProperty(e)})).map((function(t){return e.loadResources(a[t],(function(e){return"_framework/"+e}),"assembly")})).reduce((function(e,t){return e.concat(t)}),new Array).map((function(e){return r(l,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return[4,e.response];case 1:return[2,t.sent().arrayBuffer()]}}))}))})));return BINDING.js_to_mono_obj(i.then((function(e){return e.length&&(window.Blazor._internal.readSatelliteAssemblies=function(){for(var t=BINDING.mono_obj_array_new(e.length),n=0;n>1];var n},readInt32Field:function(e,t){return d(e+(t||0))},readUint64Field:function(e,t){return function(e){var t=e>>2,n=Module.HEAPU32[t+1];if(n>f)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*l+Module.HEAPU32[t]}(e+(t||0))},readFloatField:function(e,t){return n=e+(t||0),Module.HEAPF32[n>>2];var n},readObjectField:function(e,t){return d(e+(t||0))},readStringField:function(e,t,n){var r=d(e+(t||0));if(0===r)return null;if(n){var o=BINDING.unbox_mono_obj(r);return"boolean"==typeof o?o?"":null:o}return BINDING.conv_string(r)},readStructField:function(e,t){return e+(t||0)}};var p=document.createElement("a");function h(e){return e+12}function m(e,t,n){var r="["+e+"] "+t+":"+n;return BINDING.bind_static_method(r)}function v(e,t){return r(this,void 0,void 0,(function(){var n,r;return o(this,(function(o){switch(o.label){case 0:if("function"!=typeof WebAssembly.instantiateStreaming)return[3,4];o.label=1;case 1:return o.trys.push([1,3,,4]),[4,WebAssembly.instantiateStreaming(e.response,t)];case 2:return[2,o.sent().instance];case 3:return n=o.sent(),console.info("Streaming compilation failed. Falling back to ArrayBuffer instantiation. ",n),[3,4];case 4:return[4,e.response.then((function(e){return e.arrayBuffer()}))];case 5:return r=o.sent(),[4,WebAssembly.instantiate(r,t)];case 6:return[2,o.sent().instance]}}))}))}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=window.chrome&&navigator.userAgent.indexOf("Edge")<0,o=!1;function a(){return o&&r}t.hasDebuggingEnabled=a,t.attachDebuggerHotkey=function(e){o=!!e.bootConfig.resources.pdb;var t=navigator.platform.match(/^Mac/i)?"Cmd":"Alt";a()&&console.info("Debugging hotkey: Shift+"+t+"+D (when application has focus)"),document.addEventListener("keydown",(function(e){var t;e.shiftKey&&(e.metaKey||e.altKey)&&"KeyD"===e.code&&(o?r?((t=document.createElement("a")).href="_framework/debug?url="+encodeURIComponent(location.href),t.target="_blank",t.rel="noopener noreferrer",t.click()):console.error("Currently, only Microsoft Edge (80+), or Google Chrome, are supported for debugging."):console.error("Cannot start debugging, because the application was not compiled with debugging enabled."))}))}},function(e,t,n){"use strict";var r=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,a=n.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(r=a.next()).done;)i.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=a.return)&&n.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var a=n(31),i=n(32);t.loadTimezoneData=function(e){var t,n,u=new Uint8Array(e),s=a.readInt32LE(u,0);u=u.slice(4);var c=i.decodeUtf8(u.slice(0,s)),l=JSON.parse(c);u=u.slice(s),Module.FS_createPath("/","zoneinfo",!0,!0),new Set(l.map((function(e){return e[0].split("/")[0]}))).forEach((function(e){return Module.FS_createPath("/zoneinfo",e,!0,!0)}));try{for(var f=r(l),d=f.next();!d.done;d=f.next()){var p=o(d.value,2),h=p[0],m=p[1],v=u.slice(0,m);Module.FS_createDataFile("/zoneinfo/"+h,null,v,!0,!0,!0),u=u.slice(m)}}catch(e){t={error:e}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(t)throw t.error}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(17),o=function(){function e(e){this.batchAddress=e,this.arrayRangeReader=a,this.arrayBuilderSegmentReader=i,this.diffReader=u,this.editReader=s,this.frameReader=c}return e.prototype.updatedComponents=function(){return r.platform.readStructField(this.batchAddress,0)},e.prototype.referenceFrames=function(){return r.platform.readStructField(this.batchAddress,a.structLength)},e.prototype.disposedComponentIds=function(){return r.platform.readStructField(this.batchAddress,2*a.structLength)},e.prototype.disposedEventHandlerIds=function(){return r.platform.readStructField(this.batchAddress,3*a.structLength)},e.prototype.updatedComponentsEntry=function(e,t){return l(e,t,u.structLength)},e.prototype.referenceFramesEntry=function(e,t){return l(e,t,c.structLength)},e.prototype.disposedComponentIdsEntry=function(e,t){var n=l(e,t,4);return r.platform.readInt32Field(n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=l(e,t,8);return r.platform.readUint64Field(n)},e}();t.SharedMemoryRenderBatch=o;var a={structLength:8,values:function(e){return r.platform.readObjectField(e,0)},count:function(e){return r.platform.readInt32Field(e,4)}},i={structLength:12,values:function(e){var t=r.platform.readObjectField(e,0),n=r.platform.getObjectFieldsBaseAddress(t);return r.platform.readObjectField(n,0)},offset:function(e){return r.platform.readInt32Field(e,4)},count:function(e){return r.platform.readInt32Field(e,8)}},u={structLength:4+i.structLength,componentId:function(e){return r.platform.readInt32Field(e,0)},edits:function(e){return r.platform.readStructField(e,4)},editsEntry:function(e,t){return l(e,t,s.structLength)}},s={structLength:20,editType:function(e){return r.platform.readInt32Field(e,0)},siblingIndex:function(e){return r.platform.readInt32Field(e,4)},newTreeIndex:function(e){return r.platform.readInt32Field(e,8)},moveToSiblingIndex:function(e){return r.platform.readInt32Field(e,8)},removedAttributeName:function(e){return r.platform.readStringField(e,16)}},c={structLength:36,frameType:function(e){return r.platform.readInt16Field(e,4)},subtreeLength:function(e){return r.platform.readInt32Field(e,8)},elementReferenceCaptureId:function(e){return r.platform.readStringField(e,16)},componentId:function(e){return r.platform.readInt32Field(e,12)},elementName:function(e){return r.platform.readStringField(e,16)},textContent:function(e){return r.platform.readStringField(e,16)},markupContent:function(e){return r.platform.readStringField(e,16)},attributeName:function(e){return r.platform.readStringField(e,16)},attributeValue:function(e){return r.platform.readStringField(e,24,!0)},attributeEventHandlerId:function(e){return r.platform.readUint64Field(e,8)}};function l(e,t,n){return r.platform.getArrayEntryPtr(e,t,n)}},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,a){function i(e){try{s(r.next(e))}catch(e){a(e)}}function u(e){try{s(r.throw(e))}catch(e){a(e)}}function s(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,u)}s((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(a){return function(u){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1] n.startsWith('dotnet.') && n.endsWith('.js'))[0]; const dotnetJsContentHash = resourceLoader.bootConfig.resources.runtime[dotnetJsResourceName]; const scriptElem = document.createElement('script'); - scriptElem.src = `_framework/wasm/${dotnetJsResourceName}`; + scriptElem.src = `_framework/${dotnetJsResourceName}`; scriptElem.defer = true; // For consistency with WebAssemblyResourceLoader, we only enforce SRI if caching is allowed @@ -204,11 +204,11 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade // Begin loading the .dll/.pdb/.wasm files, but don't block here. Let other loading processes run in parallel. const dotnetWasmResourceName = 'dotnet.wasm'; - const assembliesBeingLoaded = resourceLoader.loadResources(resources.assembly, filename => `_framework/_bin/${filename}`, 'assembly'); - const pdbsBeingLoaded = resourceLoader.loadResources(resources.pdb || {}, filename => `_framework/_bin/${filename}`, 'pdb'); + const assembliesBeingLoaded = resourceLoader.loadResources(resources.assembly, filename => `_framework/${filename}`, 'assembly'); + const pdbsBeingLoaded = resourceLoader.loadResources(resources.pdb || {}, filename => `_framework/${filename}`, 'pdb'); const wasmBeingLoaded = resourceLoader.loadResource( /* name */ dotnetWasmResourceName, - /* url */ `_framework/wasm/${dotnetWasmResourceName}`, + /* url */ `_framework/${dotnetWasmResourceName}`, /* hash */ resourceLoader.bootConfig.resources.runtime[dotnetWasmResourceName], /* type */ 'dotnetwasm'); @@ -217,7 +217,7 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade if (resourceLoader.bootConfig.resources.runtime.hasOwnProperty(dotnetTimeZoneResourceName)) { timeZoneResource = resourceLoader.loadResource( dotnetTimeZoneResourceName, - `_framework/wasm/${dotnetTimeZoneResourceName}`, + `_framework/${dotnetTimeZoneResourceName}`, resourceLoader.bootConfig.resources.runtime[dotnetTimeZoneResourceName], 'timezonedata'); } @@ -267,7 +267,7 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade if (satelliteResources) { const resourcePromises = Promise.all(culturesToLoad .filter(culture => satelliteResources.hasOwnProperty(culture)) - .map(culture => resourceLoader.loadResources(satelliteResources[culture], fileName => `_framework/_bin/${fileName}`, 'assembly')) + .map(culture => resourceLoader.loadResources(satelliteResources[culture], fileName => `_framework/${fileName}`, 'assembly')) .reduce((previous, next) => previous.concat(next), new Array()) .map(async resource => (await resource.response).arrayBuffer())); diff --git a/src/Components/WebAssembly/Build/src/Microsoft.AspNetCore.Components.WebAssembly.Build.csproj b/src/Components/WebAssembly/Build/src/Microsoft.AspNetCore.Components.WebAssembly.Build.csproj deleted file mode 100644 index d15e4ca761..0000000000 --- a/src/Components/WebAssembly/Build/src/Microsoft.AspNetCore.Components.WebAssembly.Build.csproj +++ /dev/null @@ -1,112 +0,0 @@ - - - - $(DefaultNetCoreTargetFramework);net46 - Microsoft.AspNetCore.Components.WebAssembly.Build.Tasks - Microsoft.AspNetCore.Components.WebAssembly.Build - Build mechanism for ASP.NET Core Blazor WebAssembly applications. - true - false - true - - - - - - false - true - Microsoft.AspNetCore.Components.WebAssembly.Build.nuspec - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_NetCoreFilesToCopy Include="$(OutputPath)$(DefaultNetCoreTargetFramework)\*" TargetPath="netcoreapp\" /> - <_DesktopFilesToCopy Include="$(OutputPath)net46\*" TargetPath="netfx\" /> - <_AllFilesToCopy Include="@(_NetCoreFilesToCopy);@(_DesktopFilesToCopy)" /> - - - - - - - - - - - - <_BrotliToolPathInput Include="..\..\Compression\src\Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression.csproj" /> - <_BrotliToolPathInput Include="..\..\Compression\src\*.cs" /> - <_BrotliToolPathInput Include="..\..\Compression\src\*.runtimeconfig.json" /> - <_BrotliToolPathOutput Include="$(MSBuildThisFileDirectory)bin\$(Configuration)\tools\compression\blazor-brotli.dll" /> - - - - - <_BrotliToolsPath Include="$(MSBuildThisFileDirectory)bin\$(Configuration)\tools\compression\" /> - - - - <_BrotliToolsOutputPath>@(_BrotliToolsPath->'%(FullPath)') - - - - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/src/Microsoft.AspNetCore.Components.WebAssembly.Build.nuspec b/src/Components/WebAssembly/Build/src/Microsoft.AspNetCore.Components.WebAssembly.Build.nuspec deleted file mode 100644 index 9b8aa9ad3a..0000000000 --- a/src/Components/WebAssembly/Build/src/Microsoft.AspNetCore.Components.WebAssembly.Build.nuspec +++ /dev/null @@ -1,19 +0,0 @@ - - - - $CommonMetadataElements$ - - - - - - $CommonFileElements$ - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/src/ReferenceBlazorBuildFromSource.props b/src/Components/WebAssembly/Build/src/ReferenceBlazorBuildFromSource.props deleted file mode 100644 index 2083ec2891..0000000000 --- a/src/Components/WebAssembly/Build/src/ReferenceBlazorBuildFromSource.props +++ /dev/null @@ -1,27 +0,0 @@ - - - - - $(MSBuildThisFileDirectory)..\..\..\ - $(Configuration) - - <_BlazorJsPath>$(ComponentsRoot)Web.JS\dist\$(BlazorBuildConfiguration)\blazor.webassembly.js - <_BlazorJsMapPath>$(ComponentsRoot)Web.JS\dist\$(BlazorBuildConfiguration)\blazor.webassembly.js.map - <_BlazorToolsDir>$(MSBuildThisFileDirectory)bin\$(BlazorBuildConfiguration)\tools\ - - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/src/ReferenceFromSource.props b/src/Components/WebAssembly/Build/src/ReferenceFromSource.props deleted file mode 100644 index decf4343ee..0000000000 --- a/src/Components/WebAssembly/Build/src/ReferenceFromSource.props +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - dotnet - <_BlazorCliLocation>$(MSBuildThisFileDirectory)../../DevServer/src/bin/$(Configuration)/$(DefaultNetCoreTargetFramework)/blazor-devserver.dll - exec "$(_BlazorCliLocation)" serve --applicationpath "$(MSBuildProjectDirectory)/$(OutputPath)$(TargetFileName)" $(AdditionalRunArguments) - - - - - - - - - - - - - - - - false - true - TargetFramework=$(DefaultNetCoreTargetFramework) - - - false - - true - TargetFramework - - - diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorGetFileHash.cs b/src/Components/WebAssembly/Build/src/Tasks/BlazorGetFileHash.cs deleted file mode 100644 index f10f53a748..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/BlazorGetFileHash.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Security.Cryptography; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.Build.Tasks -{ - /// - /// Computes the checksum for a single file. - /// - public sealed class BlazorGetFileHash : Task - { - internal const string _defaultFileHashAlgorithm = "SHA256"; - internal const string _hashEncodingBase64 = "base64"; - - /// - /// The files to be hashed. - /// - [Required] - public ITaskItem[] Files { get; set; } - - /// - /// The algorithm. Allowed values: SHA256, SHA384, SHA512. Default = SHA256. - /// - public string Algorithm { get; set; } = _defaultFileHashAlgorithm; - - /// - /// The metadata name where the hash is stored in each item. Defaults to "FileHash". - /// - public string MetadataName { get; set; } = "FileHash"; - - /// - /// The encoding to use for generated hashs. Defaults to "hex". Allowed values = "base64". - /// - public string HashEncoding { get; set; } = _hashEncodingBase64; - - /// - /// The hash of the file. This is only set if there was one item group passed in. - /// - [Output] - public string Hash { get; set; } - - /// - /// The input files with additional metadata set to include the file hash. - /// - [Output] - public ITaskItem[] Items { get; set; } - - public override bool Execute() - { - if (!SupportsAlgorithm(Algorithm)) - { - Log.LogError("Unrecognized HashAlgorithm {0}", Algorithm); - return false; - } - - System.Threading.Tasks.Parallel.ForEach(Files, file => - { - if (!File.Exists(file.ItemSpec)) - { - Log.LogError("File not found '{0}", file.ItemSpec); - } - }); - - if (Log.HasLoggedErrors) - { - return false; - } - - System.Threading.Tasks.Parallel.ForEach(Files, file => - { - var hash = ComputeHash(Algorithm, file.ItemSpec); - file.SetMetadata("FileHashAlgorithm", Algorithm); - file.SetMetadata(MetadataName, EncodeHash(hash)); - }); - - Items = Files; - - if (Files.Length == 1) - { - Hash = Files[0].GetMetadata(MetadataName); - } - - return !Log.HasLoggedErrors; - } - - internal static string EncodeHash(byte[] hash) - { - return Convert.ToBase64String(hash); - } - - internal static bool SupportsAlgorithm(string algorithmName) - => _supportedAlgorithms.Contains(algorithmName); - - internal static byte[] ComputeHash(string algorithmName, string filePath) - { - using (var stream = File.OpenRead(filePath)) - using (var algorithm = CreateAlgorithm(algorithmName)) - { - return algorithm.ComputeHash(stream); - } - } - - private static readonly HashSet _supportedAlgorithms - = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "SHA256", - "SHA384", - "SHA512", - }; - - private static HashAlgorithm CreateAlgorithm(string algorithmName) - { - if (string.Equals(algorithmName, "SHA256", StringComparison.OrdinalIgnoreCase)) - { - return SHA256.Create(); - } - else if (string.Equals(algorithmName, "SHA384", StringComparison.OrdinalIgnoreCase)) - { - return SHA384.Create(); - } - else if (string.Equals(algorithmName, "SHA512", StringComparison.OrdinalIgnoreCase)) - { - return SHA512.Create(); - } - - throw new ArgumentOutOfRangeException(); - } - } -} \ No newline at end of file diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorILLink.cs b/src/Components/WebAssembly/Build/src/Tasks/BlazorILLink.cs deleted file mode 100644 index 0244e9f0c2..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/BlazorILLink.cs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build.Tasks -{ - // Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/LinkTask.cs - public class BlazorILLink : ToolTask - { - private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; - - [Required] - public string ILLinkPath { get; set; } - - [Required] - public ITaskItem[] AssemblyPaths { get; set; } - - public ITaskItem[] ReferenceAssemblyPaths { get; set; } - - [Required] - public ITaskItem[] RootAssemblyNames { get; set; } - - [Required] - public ITaskItem OutputDirectory { get; set; } - - public ITaskItem[] RootDescriptorFiles { get; set; } - - public bool ClearInitLocals { get; set; } - - public string ClearInitLocalsAssemblies { get; set; } - - public string ExtraArgs { get; set; } - - public bool DumpDependencies { get; set; } - - private string _dotnetPath; - - private string DotNetPath - { - get - { - if (!string.IsNullOrEmpty(_dotnetPath)) - { - return _dotnetPath; - } - - _dotnetPath = Environment.GetEnvironmentVariable(DotNetHostPathEnvironmentName); - if (string.IsNullOrEmpty(_dotnetPath)) - { - throw new InvalidOperationException($"{DotNetHostPathEnvironmentName} is not set"); - } - - return _dotnetPath; - } - } - - protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High; - - protected override string ToolName => Path.GetFileName(DotNetPath); - - protected override string GenerateFullPathToTool() => DotNetPath; - - protected override string GenerateCommandLineCommands() - { - var args = new StringBuilder(); - args.Append(Quote(ILLinkPath)); - return args.ToString(); - } - - private static string Quote(string path) - { - return $"\"{path.TrimEnd('\\')}\""; - } - - protected override string GenerateResponseFileCommands() - { - var args = new StringBuilder(); - - if (RootDescriptorFiles != null) - { - foreach (var rootFile in RootDescriptorFiles) - { - args.Append("-x ").AppendLine(Quote(rootFile.ItemSpec)); - } - } - - foreach (var assemblyItem in RootAssemblyNames) - { - args.Append("-a ").AppendLine(Quote(assemblyItem.ItemSpec)); - } - - var assemblyNames = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var assembly in AssemblyPaths) - { - var assemblyPath = assembly.ItemSpec; - var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); - - // If there are multiple paths with the same assembly name, only use the first one. - if (!assemblyNames.Add(assemblyName)) - { - continue; - } - - args.Append("-reference ") - .AppendLine(Quote(assemblyPath)); - - var action = assembly.GetMetadata("action"); - if ((action != null) && (action.Length > 0)) - { - args.Append("-p "); - args.Append(action); - args.Append(" ").AppendLine(Quote(assemblyName)); - } - } - - if (ReferenceAssemblyPaths != null) - { - foreach (var assembly in ReferenceAssemblyPaths) - { - var assemblyPath = assembly.ItemSpec; - var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); - - // Don't process references for which we already have - // implementation assemblies. - if (assemblyNames.Contains(assemblyName)) - { - continue; - } - - args.Append("-reference ").AppendLine(Quote(assemblyPath)); - - // Treat reference assemblies as "skip". Ideally we - // would not even look at the IL, but only use them to - // resolve surface area. - args.Append("-p skip ").AppendLine(Quote(assemblyName)); - } - } - - if (OutputDirectory != null) - { - args.Append("-out ").AppendLine(Quote(OutputDirectory.ItemSpec)); - } - - if (ClearInitLocals) - { - args.AppendLine("--enable-opt clearinitlocals"); - if ((ClearInitLocalsAssemblies != null) && (ClearInitLocalsAssemblies.Length > 0)) - { - args.Append("-m ClearInitLocalsAssemblies "); - args.AppendLine(ClearInitLocalsAssemblies); - } - } - - if (ExtraArgs != null) - { - args.AppendLine(ExtraArgs); - } - - if (DumpDependencies) - { - args.AppendLine("--dump-dependencies"); - } - - return args.ToString(); - } - - protected override bool HandleTaskExecutionErrors() - { - // Show a slightly better error than the standard ToolTask message that says "dotnet" failed. - Log.LogError($"ILLink failed with exit code {ExitCode}."); - return false; - } - - protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) - { - if (!string.IsNullOrEmpty(singleLine) && singleLine.StartsWith("Unhandled exception.", StringComparison.Ordinal)) - { - // The Mono linker currently prints out an entire stack trace when the linker fails. - // We want to show something actionable in the VS Error window. - Log.LogError(singleLine); - } - else - { - base.LogEventsFromTextOutput(singleLine, messageImportance); - } - } - } -} diff --git a/src/Components/WebAssembly/Build/src/Tasks/BrotliCompressBlazorApplicationFiles.cs b/src/Components/WebAssembly/Build/src/Tasks/BrotliCompressBlazorApplicationFiles.cs deleted file mode 100644 index fc4a4e6ad9..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/BrotliCompressBlazorApplicationFiles.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class BrotliCompressBlazorApplicationFiles : ToolTask - { - private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; - - [Required] - public string ManifestPath { get; set; } - - [Required] - public string BlazorBrotliPath { get; set; } - - private string _dotnetPath; - - private string DotNetPath - { - get - { - if (!string.IsNullOrEmpty(_dotnetPath)) - { - return _dotnetPath; - } - - _dotnetPath = Environment.GetEnvironmentVariable(DotNetHostPathEnvironmentName); - if (string.IsNullOrEmpty(_dotnetPath)) - { - throw new InvalidOperationException($"{DotNetHostPathEnvironmentName} is not set"); - } - - return _dotnetPath; - } - } - - protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High; - - protected override string ToolName => Path.GetFileName(DotNetPath); - - protected override string GenerateFullPathToTool() => DotNetPath; - - protected override string GenerateCommandLineCommands() => - $"\"{BlazorBrotliPath}\" \"{ManifestPath}\""; - } -} diff --git a/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs b/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs deleted file mode 100644 index d3f1de66a9..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class GenerateBlazorBootJson : 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 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 - internal 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) - { - var resourceTypeMetadata = resource.GetMetadata("BootManifestResourceType"); - ResourceHashesByNameDictionary resourceList; - switch (resourceTypeMetadata) - { - case "runtime": - resourceList = resourceData.runtime; - break; - case "assembly": - resourceList = resourceData.assembly; - break; - case "dynamicAssembly": - resourceList = resourceData.dynamicAssembly; - break; - case "pdb": - resourceData.pdb ??= new ResourceHashesByNameDictionary(); - resourceList = resourceData.pdb; - break; - case "satellite": - if (resourceData.satelliteResources is null) - { - resourceData.satelliteResources = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - var resourceCulture = resource.GetMetadata("Culture"); - if (resourceCulture is null) - { - Log.LogWarning("Satellite resource {0} does not specify required metadata 'Culture'.", resource); - continue; - } - - if (!resourceData.satelliteResources.TryGetValue(resourceCulture, out resourceList)) - { - resourceList = new ResourceHashesByNameDictionary(); - resourceData.satelliteResources.Add(resourceCulture, resourceList); - } - break; - default: - throw new NotSupportedException($"Unsupported BootManifestResourceType metadata value: {resourceTypeMetadata}"); - } - - var resourceName = GetResourceName(resource); - if (!resourceList.ContainsKey(resourceName)) - { - resourceList.Add(resourceName, $"sha256-{resource.GetMetadata("Integrity")}"); - } - } - } - - 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 static string GetResourceName(ITaskItem item) - { - var name = item.GetMetadata("BootManifestResourceName"); - - if (string.IsNullOrEmpty(name)) - { - throw new Exception($"No BootManifestResourceName was specified for item '{item.ItemSpec}'"); - } - - return name.Replace('\\', '/'); - } - -#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(); - - /// - /// Assembly (.dll) resources that are loaded dynamically during runtime - /// - public ResourceHashesByNameDictionary dynamicAssembly { 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; } - } -#pragma warning restore IDE1006 // Naming Styles - } -} \ No newline at end of file diff --git a/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorCompressionManifest.cs b/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorCompressionManifest.cs deleted file mode 100644 index e7a7b16ee2..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorCompressionManifest.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class GenerateBlazorCompressionManifest : Task - { - [Required] - public ITaskItem[] FilesToCompress { get; set; } - - [Required] - public string ManifestPath { get; set; } - - public override bool Execute() - { - try - { - WriteCompressionManifest(); - } - catch (Exception ex) - { - Log.LogErrorFromException(ex); - } - - return !Log.HasLoggedErrors; - } - - private void WriteCompressionManifest() - { - var tempFilePath = Path.GetTempFileName(); - - var manifest = new ManifestData(); - var filesToCompress = new List(); - - foreach (var file in FilesToCompress) - { - filesToCompress.Add(new CompressedFile - { - Source = file.GetMetadata("FullPath"), - InputSource = file.GetMetadata("InputSource"), - Target = file.GetMetadata("TargetCompressionPath"), - }); - } - - manifest.FilesToCompress = filesToCompress.ToArray(); - - var serializer = new DataContractJsonSerializer(typeof(ManifestData)); - - using (var tempFile = File.OpenWrite(tempFilePath)) - { - using (var writer = JsonReaderWriterFactory.CreateJsonWriter(tempFile, Encoding.UTF8, ownsStream: false, indent: true)) - { - serializer.WriteObject(writer, manifest); - } - } - - if (!File.Exists(ManifestPath)) - { - File.Move(tempFilePath, ManifestPath); - return; - } - - var originalText = File.ReadAllText(ManifestPath); - var newManifest = File.ReadAllText(tempFilePath); - if (!string.Equals(originalText, newManifest, StringComparison.Ordinal)) - { - // OnlyWriteWhenDifferent - File.Delete(ManifestPath); - File.Move(tempFilePath, ManifestPath); - } - } - - [DataContract] - private class ManifestData - { - [DataMember] - public CompressedFile[] FilesToCompress { get; set; } - } - - [DataContract] - private class CompressedFile - { - [DataMember] - public string Source { get; set; } - - [DataMember] - public string InputSource { get; set; } - - [DataMember] - public string Target { get; set; } - } - } -} diff --git a/src/Components/WebAssembly/Build/src/Tasks/GenerateServiceWorkerAssetsManifest.cs b/src/Components/WebAssembly/Build/src/Tasks/GenerateServiceWorkerAssetsManifest.cs deleted file mode 100644 index 9a1885ad0c..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/GenerateServiceWorkerAssetsManifest.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using System.Linq; -using System.Runtime.Serialization.Json; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.AspNetCore.Blazor.Build -{ - public class GenerateServiceWorkerAssetsManifest : Task - { - [Required] - public string Version { get; set; } - - [Required] - public ITaskItem[] AssetsWithHashes { get; set; } - - [Required] - public string OutputPath { get; set; } - - public override bool Execute() - { - using var fileStream = File.Create(OutputPath); - WriteFile(fileStream); - return true; - } - - internal void WriteFile(Stream stream) - { - var data = new AssetsManifestFile - { - version = Version, - assets = AssetsWithHashes.Select(item => new AssetsManifestFileEntry - { - url = item.GetMetadata("AssetUrl"), - hash = $"sha256-{item.GetMetadata("Integrity")}", - }).ToArray() - }; - - 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(";"); - } - -#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/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs b/src/Components/WebAssembly/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs deleted file mode 100644 index 66e673501e..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using System.Xml.Linq; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build.Tasks -{ - public class GenerateTypeGranularityLinkingConfig : Task - { - [Required] - public ITaskItem[] Assemblies { get; set; } - - [Required] - public string OutputPath { get; set; } - - public override bool Execute() - { - var linkerElement = new XElement("linker", - new XComment(" THIS IS A GENERATED FILE - DO NOT EDIT MANUALLY ")); - - foreach (var assembly in Assemblies) - { - var assemblyElement = CreateTypeGranularityConfig(assembly); - linkerElement.Add(assemblyElement); - } - - using var fileStream = File.Open(OutputPath, FileMode.Create); - new XDocument(linkerElement).Save(fileStream); - - return true; - } - - private XElement CreateTypeGranularityConfig(ITaskItem assembly) - { - // We match all types in the assembly, and for each one, tell the linker to preserve all - // its members (preserve=all) but only if there's some reference to the type (required=false) - return new XElement("assembly", - new XAttribute("fullname", Path.GetFileNameWithoutExtension(assembly.ItemSpec)), - new XElement("type", - new XAttribute("fullname", "*"), - new XAttribute("preserve", "all"), - new XAttribute("required", "false"))); - } - } -} diff --git a/src/Components/WebAssembly/Build/src/Tasks/GzipCompressBlazorApplicationFiles.cs b/src/Components/WebAssembly/Build/src/Tasks/GzipCompressBlazorApplicationFiles.cs deleted file mode 100644 index 53d26328a3..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/GzipCompressBlazorApplicationFiles.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; -using System.IO.Compression; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class GzipCompressBlazorApplicationFiles : Task - { - [Required] - public string ManifestPath { get; set; } - - public override bool Execute() - { - var serializer = new DataContractJsonSerializer(typeof(ManifestData)); - - ManifestData manifest = null; - using (var tempFile = File.OpenRead(ManifestPath)) - { - manifest = (ManifestData)serializer.ReadObject(tempFile); - } - - System.Threading.Tasks.Parallel.ForEach(manifest.FilesToCompress, (file) => - { - var inputPath = file.Source; - var inputSource = file.InputSource; - var targetCompressionPath = file.Target; - - if (!File.Exists(inputSource)) - { - Log.LogMessage($"Skipping '{inputPath}' because '{inputSource}' does not exist."); - return; - } - - if (File.Exists(targetCompressionPath) && File.GetLastWriteTimeUtc(inputSource) < File.GetLastWriteTimeUtc(targetCompressionPath)) - { - // Incrementalism. If input source doesn't exist or it exists and is not newer than the expected output, do nothing. - Log.LogMessage($"Skipping '{inputPath}' because '{targetCompressionPath}' is newer than '{inputSource}'."); - return; - } - - try - { - Directory.CreateDirectory(Path.GetDirectoryName(targetCompressionPath)); - - using var sourceStream = File.OpenRead(inputPath); - using var fileStream = new FileStream(targetCompressionPath, FileMode.Create); - using var stream = new GZipStream(fileStream, CompressionLevel.Optimal); - - sourceStream.CopyTo(stream); - } - catch (Exception e) - { - Log.LogErrorFromException(e); - throw; - } - }); - - return !Log.HasLoggedErrors; - } - - [DataContract] - private class ManifestData - { - [DataMember] - public CompressedFile[] FilesToCompress { get; set; } - } - - [DataContract] - private class CompressedFile - { - [DataMember] - public string Source { get; set; } - - [DataMember] - public string InputSource { get; set; } - - [DataMember] - public string Target { get; set; } - } - } -} - diff --git a/src/Components/WebAssembly/Build/src/Tasks/ResolveBlazorRuntimeDependencies.cs b/src/Components/WebAssembly/Build/src/Tasks/ResolveBlazorRuntimeDependencies.cs deleted file mode 100644 index 4598432a1c..0000000000 --- a/src/Components/WebAssembly/Build/src/Tasks/ResolveBlazorRuntimeDependencies.cs +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class ResolveBlazorRuntimeDependencies : Task - { - [Required] - public string EntryPoint { get; set; } - - [Required] - public ITaskItem[] ApplicationDependencies { get; set; } - - [Required] - public ITaskItem[] WebAssemblyBCLAssemblies { get; set; } - - [Output] - public ITaskItem[] Dependencies { get; set; } - - public override bool Execute() - { - var paths = ResolveRuntimeDependenciesCore(EntryPoint, ApplicationDependencies.Select(c => c.ItemSpec), WebAssemblyBCLAssemblies.Select(c => c.ItemSpec)); - Dependencies = paths.Select(p => new TaskItem(p)).ToArray(); - - return true; - } - - public static IEnumerable ResolveRuntimeDependenciesCore( - string entryPoint, - IEnumerable applicationDependencies, - IEnumerable monoBclAssemblies) - { - var entryAssembly = new AssemblyEntry(entryPoint, GetAssemblyName(entryPoint)); - - var dependencies = CreateAssemblyLookup(applicationDependencies); - - var bcl = CreateAssemblyLookup(monoBclAssemblies); - - var assemblyResolutionContext = new AssemblyResolutionContext( - entryAssembly, - dependencies, - bcl); - - assemblyResolutionContext.ResolveAssemblies(); - - var paths = assemblyResolutionContext.Results.Select(r => r.Path); - return paths.Concat(FindPdbs(paths)); - - static Dictionary CreateAssemblyLookup(IEnumerable assemblyPaths) - { - var dictionary = new Dictionary(StringComparer.Ordinal); - foreach (var path in assemblyPaths) - { - var assemblyName = AssemblyName.GetAssemblyName(path).Name; - if (dictionary.TryGetValue(assemblyName, out var previous)) - { - throw new InvalidOperationException($"Multiple assemblies found with the same assembly name '{assemblyName}':" + - Environment.NewLine + string.Join(Environment.NewLine, previous, path)); - } - dictionary[assemblyName] = new AssemblyEntry(path, assemblyName); - } - - return dictionary; - } - } - - private static string GetAssemblyName(string assemblyPath) - { - return AssemblyName.GetAssemblyName(assemblyPath).Name; - } - - private static IEnumerable FindPdbs(IEnumerable dllPaths) - { - return dllPaths - .Select(path => Path.ChangeExtension(path, "pdb")) - .Where(path => File.Exists(path)); - } - - public class AssemblyResolutionContext - { - public AssemblyResolutionContext( - AssemblyEntry entryAssembly, - Dictionary dependencies, - Dictionary bcl) - { - EntryAssembly = entryAssembly; - Dependencies = dependencies; - Bcl = bcl; - } - - public AssemblyEntry EntryAssembly { get; } - public Dictionary Dependencies { get; } - public Dictionary Bcl { get; } - - public IList Results { get; } = new List(); - - internal void ResolveAssemblies() - { - var visitedAssemblies = new HashSet(); - var pendingAssemblies = new Stack(); - pendingAssemblies.Push(EntryAssembly.Name); - ResolveAssembliesCore(); - - void ResolveAssembliesCore() - { - while (pendingAssemblies.Count > 0) - { - var current = pendingAssemblies.Pop(); - if (visitedAssemblies.Add(current)) - { - // Not all references will be resolvable within the Mono BCL. - // Skipping unresolved assemblies here is equivalent to passing "--skip-unresolved true" to the Mono linker. - if (Resolve(current) is AssemblyEntry resolved) - { - Results.Add(resolved); - var references = GetAssemblyReferences(resolved.Path); - foreach (var reference in references) - { - pendingAssemblies.Push(reference); - } - } - } - } - } - - AssemblyEntry? Resolve(string assemblyName) - { - if (EntryAssembly.Name == assemblyName) - { - return EntryAssembly; - } - - // Resolution logic. For right now, we will prefer the mono BCL version of a given - // assembly if there is a candidate assembly and an equivalent mono assembly. - if (Bcl.TryGetValue(assemblyName, out var assembly) || - Dependencies.TryGetValue(assemblyName, out assembly)) - { - return assembly; - } - - return null; - } - - static IReadOnlyList GetAssemblyReferences(string assemblyPath) - { - try - { - using var peReader = new PEReader(File.OpenRead(assemblyPath)); - if (!peReader.HasMetadata) - { - return Array.Empty(); // not a managed assembly - } - - var metadataReader = peReader.GetMetadataReader(); - - var references = new List(); - foreach (var handle in metadataReader.AssemblyReferences) - { - var reference = metadataReader.GetAssemblyReference(handle); - var referenceName = metadataReader.GetString(reference.Name); - - references.Add(referenceName); - } - - return references; - } - catch (BadImageFormatException) - { - // not a PE file, or invalid metadata - } - - return Array.Empty(); // not a managed assembly - } - } - } - - [DebuggerDisplay("{ToString(),nq}")] - public readonly struct AssemblyEntry - { - public AssemblyEntry(string path, string name) - { - Path = path; - Name = name; - } - - public string Path { get; } - public string Name { get; } - - public override string ToString() => Name; - } - } -} diff --git a/src/Components/WebAssembly/Build/src/build/netstandard1.0/Microsoft.AspNetCore.Components.WebAssembly.Build.props b/src/Components/WebAssembly/Build/src/build/netstandard1.0/Microsoft.AspNetCore.Components.WebAssembly.Build.props deleted file mode 100644 index a85c05ce87..0000000000 --- a/src/Components/WebAssembly/Build/src/build/netstandard1.0/Microsoft.AspNetCore.Components.WebAssembly.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/Components/WebAssembly/Build/src/build/netstandard1.0/Microsoft.AspNetCore.Components.WebAssembly.Build.targets b/src/Components/WebAssembly/Build/src/build/netstandard1.0/Microsoft.AspNetCore.Components.WebAssembly.Build.targets deleted file mode 100644 index 35115b33b3..0000000000 --- a/src/Components/WebAssembly/Build/src/build/netstandard1.0/Microsoft.AspNetCore.Components.WebAssembly.Build.targets +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/Components/WebAssembly/Build/src/targets/All.props b/src/Components/WebAssembly/Build/src/targets/All.props deleted file mode 100644 index 8315d3cb40..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/All.props +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - / - - - true - - true - - diff --git a/src/Components/WebAssembly/Build/src/targets/All.targets b/src/Components/WebAssembly/Build/src/targets/All.targets deleted file mode 100644 index 5966cec27f..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/All.targets +++ /dev/null @@ -1,21 +0,0 @@ - - - <_BlazorToolsDir Condition="'$(_BlazorToolsDir)' == ''">$(MSBuildThisFileDirectory)..\tools\ - <_BlazorTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp - <_BlazorTasksTFM Condition=" '$(_BlazorTasksTFM)' == ''">netfx - <_BlazorTasksPath>$(_BlazorToolsDir)$(_BlazorTasksTFM)\Microsoft.AspNetCore.Components.WebAssembly.Build.Tasks.dll - - - true - - - true - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.props b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.props deleted file mode 100644 index 453deddacd..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.props +++ /dev/null @@ -1,20 +0,0 @@ - - - - none - --disable-opt unreachablebodies --verbose --strip-security true --exclude-feature com -v false -c link -u link -b true - - <_BlazorJsPath Condition="'$(_BlazorJsPath)' == ''">$(MSBuildThisFileDirectory)..\tools\blazor\blazor.webassembly.js - <_BaseBlazorRuntimeOutputPath>_framework\ - <_BlazorRuntimeBinOutputPath>$(_BaseBlazorRuntimeOutputPath)_bin\ - <_BlazorRuntimeWasmOutputPath>$(_BaseBlazorRuntimeOutputPath)wasm\ - <_BlazorBuiltInBclLinkerDescriptor>$(MSBuildThisFileDirectory)BuiltInBclLinkerDescriptor.xml - <_BlazorCollationLinkerDescriptor>$(MSBuildThisFileDirectory)CollationLinkerDescriptor.xml - <_BlazorBootJsonName>blazor.boot.json - - - - - - - diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets deleted file mode 100644 index c143445685..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets +++ /dev/null @@ -1,489 +0,0 @@ - - - - - true - false - - - - - - - - - - - <_BlazorIntermediateOutputPath>$(IntermediateOutputPath)blazor\ - - - <_GeneratedBlazorLinkerDescriptor>$(_BlazorIntermediateOutputPath)linker.descriptor.xml - - <_TypeGranularityLinkerDescriptor>$(_BlazorIntermediateOutputPath)linker.typegranularityconfig.xml - - - <_BlazorIntermediateLinkerOutputPath>$(_BlazorIntermediateOutputPath)linker/ - - - <_BlazorBootJsonIntermediateOutputPath>$(_BlazorIntermediateOutputPath)$(_BlazorBootJsonName) - - <_BlazorLinkerOutputCache>$(_BlazorIntermediateOutputPath)linker.output - - <_BlazorApplicationAssembliesCacheFile>$(_BlazorIntermediateOutputPath)unlinked.output - - - - - <_DotNetHostDirectory>$(NetCoreRoot) - <_DotNetHostFileName>dotnet - <_DotNetHostFileName Condition=" '$(OS)' == 'Windows_NT' ">dotnet.exe - - - - <_WebAssemblyBCLFolder Include=" - $(ComponentsWebAssemblyBaseClassLibraryPath); - $(ComponentsWebAssemblyBaseClassLibraryFacadesPath); - $(ComponentsWebAssemblyFrameworkPath)" /> - - <_WebAssemblyBCLAssembly Include="%(_WebAssemblyBCLFolder.Identity)*.dll" /> - - <_BlazorConfigFile Include="wwwroot\appsettings*.json" /> - - - - - - <_BlazorManagedRuntimeAssembly Include="@(RuntimeCopyLocalItems)" /> - - - <_BlazorUserRuntimeAssembly Include="@(ReferencePath->WithMetadataValue('CopyLocal', 'true'))" /> - <_BlazorUserRuntimeAssembly Include="@(ReferenceDependencyPaths->WithMetadataValue('CopyLocal', 'true'))" /> - - <_BlazorManagedRuntimeAssembly Include="@(_BlazorUserRuntimeAssembly)" /> - <_BlazorManagedRuntimeAssembly Include="@(IntermediateAssembly)" /> - - - - - - - - - - - - <_BlazorJSFile Include="$(_BlazorJSPath)" /> - <_BlazorJSFile Include="$(_BlazorJSMapPath)" Condition="Exists('$(_BlazorJSMapPath)')" /> - - <_DotNetWasmRuntimeFile Include="$(ComponentsWebAssemblyRuntimePath)*"/> - <_DotNetWasmRuntimeFile - Remove="%(Identity)" - Condition="'$(BlazorEnableTimeZoneSupport)' == 'false' AND '%(FileName)%(Extension)' == 'dotnet.timezones.dat'" /> - - - <_BlazorCopyLocalPaths Include="@(ReferenceCopyLocalPaths)" - Exclude="@(_BlazorManagedRuntimeAssembly);@(ReferenceSatellitePaths)" - Condition="'%(Extension)' == '.dll'" /> - - <_BlazorCopyLocalPaths Include="@(IntermediateSatelliteAssembliesWithTargetPath)"> - %(IntermediateSatelliteAssembliesWithTargetPath.Culture)\ - - - <_BlazorOutputWithTargetPath Include="@(_BlazorCopyLocalPaths)"> - - pdb - assembly - satellite - %(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension) - $(_BlazorRuntimeBinOutputPath)%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension) - - - <_BlazorOutputWithTargetPath Include="@(ReferenceSatellitePaths)"> - $([System.String]::Copy('%(ReferenceSatellitePaths.DestinationSubDirectory)').Trim('\').Trim('/')) - satellite - %(ReferenceSatellitePaths.DestinationSubDirectory)%(FileName)%(Extension) - $(_BlazorRuntimeBinOutputPath)%(ReferenceSatellitePaths.DestinationSubDirectory)%(FileName)%(Extension) - - - <_BlazorOutputWithTargetPath Include="@(_BlazorResolvedAssembly)"> - assembly - pdb - dynamicAssembly - %(FileName)%(Extension) - $(_BlazorRuntimeBinOutputPath)%(FileName)%(Extension) - - - <_BlazorOutputWithTargetPath Include="@(_DotNetWasmRuntimeFile)"> - $(_BlazorRuntimeWasmOutputPath)%(FileName)%(Extension) - runtime - %(FileName)%(Extension) - - - <_BlazorOutputWithTargetPath Include="@(_BlazorJSFile)"> - $(_BaseBlazorRuntimeOutputPath)%(FileName)%(Extension) - - - <_BlazorWriteSatelliteAssembly Include="@(_BlazorOutputWithTargetPath->WithMetadataValue('BootManifestResourceType', 'satellite'))" /> - - - - - - <_BlazorSatelliteAssemblyStashFile>$(_BlazorIntermediateOutputPath)blazor.satelliteasm.props - - - - - - - - - - - - - - - - - <_BlazorOutputWithTargetPath Include="@(_BlazorReadSatelliteAssembly)"> - satellite - %(_BlazorReadSatelliteAssembly.DestinationSubDirectory)%(FileName)%(Extension) - $(_BlazorRuntimeBinOutputPath)%(_BlazorReadSatelliteAssembly.DestinationSubDirectory)%(FileName)%(Extension) - - - - - - <_BlazorOutputWithTargetPath Remove="@(_BlazorOutputWithTargetPath)" Condition="'%(Extension)' == '.pdb'" /> - - - - <_ExistingBlazorOutputWithTargetPath Include="@(_BlazorOutputWithTargetPath)" Condition="Exists('%(FullPath)')" /> - - - - - - - - <_BlazorOutputWithIntegrity Include="@(_BlazorOutputWithHash)"> - %(_BlazorOutputWithHash.FileHash) - $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash - - - <_BlazorOutputWithTargetPath Remove="@(_BlazorOutputWithIntegrity)" /> - <_BlazorOutputWithTargetPath Include="@(_BlazorOutputWithIntegrity)" RemoveMetadata="FileHash;FileHashAlgorithm" /> - - - - - - - - - - - - - - - - - - - - - - - - - <_BlazorRuntimeCopyLocalItems Include="@(RuntimeCopyLocalItems)" /> - - - <_BlazorRuntimeCopyLocalItems IsLinkable="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('System.'))" /> - <_BlazorRuntimeCopyLocalItems IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))" /> - <_BlazorRuntimeCopyLocalItems IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.'))" /> - - <_BlazorAssemblyToLink Include="@(_WebAssemblyBCLAssembly)" /> - <_BlazorAssemblyToLink Include="@(_BlazorRuntimeCopyLocalItems)" Condition="'%(_BlazorRuntimeCopyLocalItems.IsLinkable)' == 'true'" /> - - <_BlazorLinkerRoot Include="@(IntermediateAssembly)" /> - <_BlazorLinkerRoot Include="@(_BlazorUserRuntimeAssembly)" /> - <_BlazorLinkerRoot Include="@(_BlazorRuntimeCopyLocalItems)" Condition="'%(_BlazorRuntimeCopyLocalItems.IsLinkable)' != 'true'" /> - - - - - $(AdditionalMonoLinkerOptions) --substitutions "$(_BlazorCollationLinkerDescriptor)" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_BlazorLinkerAdditionalOptions>-l $(BlazorWebAssemblyI18NAssemblies) $(AdditionalMonoLinkerOptions) - - - - <_OldLinkedFile Include="$(_BlazorIntermediateLinkerOutputPath)*.dll" /> - <_OldLinkedFile Include="$(_BlazorIntermediateLinkerOutputPath)*.pdb" /> - - - - - - - - <_LinkerResult Include="$(_BlazorIntermediateLinkerOutputPath)*.dll" /> - <_LinkerResult Include="$(_BlazorIntermediateLinkerOutputPath)*.pdb" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_BlazorResolvedAssemblyUnlinked Include="$(ComponentsWebAssemblyBaseClassLibraryPath)I18N*.dll" /> - - - - - - - - - - - - - - - - - <_BlazorBootJsonHashInput Include="@(IntermediateAssembly)" /> - <_BlazorBootJsonHashInput Include="@(_BlazorOutputWithTargetPath)" /> - <_BlazorBootJsonHashInput Include="@(_BlazorConfigFile)" /> - - - - - - - - <_BlazorBootJsonInputHashFile>$(_BlazorIntermediateOutputPath)boot.json.input - - - - - - - - - - - - <_IsDebugBuild>false - <_IsDebugBuild Condition="'$(Configuration)' == 'Debug'">true - true - - - - <_BlazorBootResource Include="@(_BlazorOutputWithTargetPath->HasMetadata('BootManifestResourceType'))" /> - - - - - - - - - - - - - - - <_BlazorBootJsonWithIntegrity Include="@(_BlazorBootJsonWithHash)"> - %(FileHash) - $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash - - - <_BlazorOutputWithTargetPath Include="@(_BlazorBootJsonWithIntegrity)" RemoveMetadata="FileHash;FileHashAlgorithm"> - $(_BaseBlazorRuntimeOutputPath)$(_BlazorBootJsonName) - - - - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/src/targets/CollationLinkerDescriptor.xml b/src/Components/WebAssembly/Build/src/targets/CollationLinkerDescriptor.xml deleted file mode 100644 index 693bdb6768..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/CollationLinkerDescriptor.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Components/WebAssembly/Build/src/targets/Compression.targets b/src/Components/WebAssembly/Build/src/targets/Compression.targets deleted file mode 100644 index 348d62481a..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/Compression.targets +++ /dev/null @@ -1,108 +0,0 @@ - - - - <_BlazorBrotliPath>$(_BlazorToolsDir)compression\blazor-brotli.dll - - $(ResolveCurrentProjectStaticWebAssetsDependsOn); - _ResolveBlazorFilesToCompress; - - - - - - - - <_BlazorFilesIntermediateOutputPath>$(IntermediateOutputPath)compressed\ - <_GzipCompressionBlazorApplicationFilesManifestPath>$(IntermediateOutputPath)compressed\gzip.manifest.json - <_BrotliCompressionBlazorApplicationFilesManifestPath>$(IntermediateOutputPath)compressed\brotli.manifest.json - - - - - - <_CompressionCandidate Include="@(StaticWebAsset)" Condition="'%(SourceType)' == '' and $([System.String]::Copy('%(RelativePath)').Replace('\','/').StartsWith('_framework/'))" KeepDuplicates="false" /> - <_CompressionCandidateIntegrity Include="@(_BlazorOutputWithTargetPath->'%(FullPath)')" /> - <_CompressionCandidateWithIntegrity Include="%(Identity)"> - @(_CompressionCandidate->'%(SourceType)') - @(_CompressionCandidate->'%(SourceId)') - @(_CompressionCandidate->'%(ContentRoot)') - @(_CompressionCandidate->'%(BasePath)') - @(_CompressionCandidate->'%(RelativePath)') - @(_CompressionCandidateIntegrity->'%(IntegrityFile)') - - - <_GzipBlazorFileToCompress Include="@(_CompressionCandidateWithIntegrity)"> - $(_BlazorFilesIntermediateOutputPath)%(RelativePath).gz - %(RelativePath).gz - %(RelativePath).gz - - <_GzipBlazorFileToCompress Remove="@(_BlazorFileCompressExclusion)" /> - - <_BrotliBlazorFileToCompress Include="@(_CompressionCandidateWithIntegrity)"> - $(_BlazorFilesIntermediateOutputPath)%(RelativePath).br - %(RelativePath).br - %(RelativePath).br - - <_BrotliBlazorFileToCompress Remove="@(_BlazorFileCompressExclusion)" /> - - <_BlazorFileToCompress Include="@(_GzipBlazorFileToCompress)" /> - <_BlazorFileToCompress Include="@(_BrotliBlazorFileToCompress)" /> - <_BlazorFileToCompress Remove="@(_BlazorFileCompressExclusion)" /> - - <_CompressedStaticWebAsset Include="@(_BlazorFileToCompress->'%(TargetCompressionPath)')" RemoveMetadata="TargetOutputPath;TargetCompressionPath" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/src/targets/Publish.targets b/src/Components/WebAssembly/Build/src/targets/Publish.targets deleted file mode 100644 index 5847bb0aca..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/Publish.targets +++ /dev/null @@ -1,40 +0,0 @@ - - - - false - false - false - false - false - true - - - - - - - - - - - - - - - true - PreserveNewest - web.config - - - - - - \ No newline at end of file diff --git a/src/Components/WebAssembly/Build/src/targets/ServiceWorkerAssetsManifest.targets b/src/Components/WebAssembly/Build/src/targets/ServiceWorkerAssetsManifest.targets deleted file mode 100644 index c17edf60e0..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/ServiceWorkerAssetsManifest.targets +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - <_ServiceWorkerAssetsManifestIntermediateOutputPath>$([MSBuild]::MakeRelative($(MSBuildProjectDirectory), $(_BlazorIntermediateOutputPath)))$(ServiceWorkerAssetsManifest) - <_ServiceWorkerAssetsManifestFullPath>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/$(_ServiceWorkerAssetsManifestIntermediateOutputPath)')) - - - - <_BlazorOutputWithTargetPath - Include="$(_ServiceWorkerAssetsManifestFullPath)" - TargetOutputPath="$(_ServiceWorkerAssetsManifestIntermediateOutputPath)" /> - - <_ManifestStaticWebAsset Include="$(_ServiceWorkerAssetsManifestFullPath)"> - - $(PackageId) - $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) - $(StaticWebAssetBasePath) - $(ServiceWorkerAssetsManifest) - - - - <_CompressionCandidate Include="@(_ManifestStaticWebAsset)" /> - <_CompressionCandidateWithIntegrity Include="@(_ManifestStaticWebAsset)"> - $(_ServiceWorkerAssetsManifestFullPath) - - - - - - - - - - - - - - - - - - - - - - - - - - - <_ServiceWorkerManifestWithIntegrity Include="@(_ServiceWorkerManifestWithHash)"> - %(FileHash) - $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash - - - - - - - - - - <_ServiceWorkerManifestIntegrityFile>$(IntermediateOutputPath)integrity\$([System.String]::Copy('%(_ServiceWorkerManifestWithIntegrity.FileHash)').Replace('/','-').Replace('+','_')).hash - - - - <_GzipFileToPatch Include="@(_GzipBlazorFileToCompress)" Condition="'%(Identity)' == '$(_ServiceWorkerAssetsManifestFullPath)'" KeepDuplicates="false"> - $(_ServiceWorkerManifestIntegrityFile) - - - <_GzipBlazorFileToCompress Remove="@(_GzipFileToPatch)" /> - <_GzipBlazorFileToCompress Include="@(_GzipFileToPatch)" /> - - <_BrotliFileToPatch Include="@(_BrotliBlazorFileToCompress)" Condition="'%(Identity)' == '$(_ServiceWorkerAssetsManifestFullPath)'" KeepDuplicates="false"> - $(_ServiceWorkerManifestIntegrityFile) - - - <_BrotliBlazorFileToCompress Remove="@(_BrotliFileToPatch)" /> - <_BrotliBlazorFileToCompress Include="@(_BrotliFileToPatch)" /> - - - - - - - - - $([System.String]::Copy('$([System.String]::Copy('%(StaticWebAsset.BasePath)').TrimEnd('/'))/%(StaticWebAsset.RelativePath)').Replace('\','/').TrimStart('/')) - - - - - - - - - <_ServiceWorkerExclude Include="@(_StaticWebAssetIntegrity)" /> - <_ServiceWorkerItemBase Include="@(ServiceWorkerAssetsManifestItem)" /> - <_ServiceWorkerItemBase Remove="@(_ServiceWorkerExclude)" /> - <_ServiceWorkerItemHash Include="@(ServiceWorkerAssetsManifestItem)" /> - <_ServiceWorkerItemHash Remove="@(_ServiceWorkerItemBase)" /> - <_ServiceWorkerAssetsManifestItemWithHash Include="%(Identity)"> - @(_ServiceWorkerItemHash->'%(AssetUrl)') - @(_StaticWebAssetIntegrity->'%(Integrity)') - - - - - - - - <_StaticWebAssetsWithoutHash Include="@(StaticWebAsset)" Condition="'%(SourceType)' != '' or '%(ContentRoot)' == '$(_BlazorCurrentProjectWWWroot)'" /> - <_StaticWebAssetsWithoutHash Remove="@(_StaticWebAssetIntegrity)" /> - - - - - - - - <_StaticWebAssetIntegrity Include="%(_StaticWebAssetHash.Identity)"> - %(_StaticWebAssetHash.FileHash) - - - - - - - - - - <_CombinedHashIntermediatePath>$(_BlazorIntermediateOutputPath)serviceworkerhashes.txt - - - - - - - - - - - - - - $([System.String]::Copy('%(_ServiceWorkerAssetsManifestCombinedHash.FileHash)').Substring(0, 8)) - - - - - - - - - - - - - - - - <_BlazorFileCompressExclusion Include="@(_ServiceWorkerIntermediateFile->'%(FullPath)')" /> - - <_ServiceWorkerStaticWebAsset Include="@(_ServiceWorkerIntermediateFile->'%(FullPath)')"> - - $(PackageId) - $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) - $(StaticWebAssetBasePath) - %(TargetOutputPath) - - - - - - - - - - - - <_ServiceWorkerIntermediateFile Include="@(ServiceWorker->'$(IntermediateOutputPath)blazor\serviceworkers\%(Identity)')"> - %(ServiceWorker.PublishedContent) - %(ServiceWorker.Identity) - %(ServiceWorker.Identity) - $([System.String]::Copy('%(ServiceWorker.Identity)').Substring(8)) - - - - - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/src/targets/StaticWebAssets.props b/src/Components/WebAssembly/Build/src/targets/StaticWebAssets.props deleted file mode 100644 index d15d07d729..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/StaticWebAssets.props +++ /dev/null @@ -1,15 +0,0 @@ - - - - $(ResolveStaticWebAssetsInputsDependsOn); - _BlazorApplyLinkPreferencesToStaticWebAssets; - _ResolveBlazorGeneratedAssets; - - - - $(GetCurrentProjectStaticWebAssetsDependsOn); - _BlazorApplyLinkPreferencesToStaticWebAssets; - _ResolveBlazorGeneratedAssets; - - - diff --git a/src/Components/WebAssembly/Build/src/targets/StaticWebAssets.targets b/src/Components/WebAssembly/Build/src/targets/StaticWebAssets.targets deleted file mode 100644 index 9c16d9dd5a..0000000000 --- a/src/Components/WebAssembly/Build/src/targets/StaticWebAssets.targets +++ /dev/null @@ -1,165 +0,0 @@ - - - - <_BlazorCurrentProjectWWWroot>$([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)\wwwroot\')) - - - - - <_BlazorOutputCandidateAsset Include="@(_BlazorOutputWithTargetPath->'%(FullPath)')"> - - $(PackageId) - $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) - $(StaticWebAssetBasePath) - $([System.String]::Copy('%(_BlazorOutputWithTargetPath.TargetOutputPath)').Replace('\','/')) - %(_BlazorOutputWithTargetPath.Integrity) - - - <_BlazorOutputCandidateAsset Remove="@(StaticWebAsset)" /> - - <_StaticWebAssetIntegrity Include="@(_BlazorOutputCandidateAsset)" KeepMetadata="Integrity" /> - - - - - - - <_ExternalStaticWebAsset Include="@(_BlazorOutputWithTargetPath->'%(FullPath)')"> - $(PackageId) - - Generated - $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\')) - $(StaticWebAssetBasePath) - - - - - $(PackageId) - $(StaticWebAssetBasePath) - - - - - - - - - <_BlazorCopyLocalAssets - Include="@(StaticWebAsset)" - Condition="'%(SourceType)' == '' and '%(ContentRoot)' != '$(_BlazorCurrentProjectWWWroot)' and !$([System.String]::Copy('%(RelativePath)').EndsWith('.br'))" /> - <_BlazorCopyLocalAssets Remove="@(_BlazorCopyLocalExclusion)" /> - - - - - - - - - - - - <_BlazorStatisticsOutput Include="@(_BlazorCopyLocalAssets->'%(RelativePath)')" /> - - - - - - - - - - <_CurrentProjectStandalonePublishStaticWebAsset Include="%(StaticWebAsset.FullPath)" Condition="'%(StaticWebAsset.SourceType)' == ''"> - PreserveNewest - $([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)','$([MSBuild]::NormalizePath('wwwroot\%(BasePath)\%(RelativePath)'))')) - - - - - - - true - - - - - - - - - <_ContentWithWwrootLinkAttribute Include="@(Content)" Condition="'%(Content.Link)' != '' and $([System.String]::Copy('%(Content.Link)').Replace('\','/').StartsWith('wwwroot/'))" /> - - <_ContentLinkedIntoWwwroot - Include="@(_ContentWithWwrootLinkAttribute)" - Condition="@(_ContentWithWwrootLinkAttribute) != '' and '%(_ContentWithWwrootLinkAttribute.CopyToPublishDirectory)' != 'false'"> - - $([System.String]::Copy('%(Link)').Substring(8)) - - - <_OutsideContentLinkedIntoWwwroot Include="@(_ContentLinkedIntoWwwroot->'%(FullPath)')" Condition="@(_ContentLinkedIntoWwwroot) != '' and !$([System.String]::Copy('%(Identity)').Replace('\','/').StartsWith('wwwroot/'))" /> - <_WwwrootLinkedContent Include="@(_ContentLinkedIntoWwwroot->'%(FullPath)')" Condition="@(_ContentLinkedIntoWwwroot) != '' and $([System.String]::Copy('%(Identity)').Replace('\','/').StartsWith('wwwroot/'))" KeepMetadata="RelativePath" /> - - - <_NonLinkedStaticWebAssets Include="@(StaticWebAsset)" Exclude="@(_WwwrootLinkedContent)" /> - <_LinkedStaticWebAssets Include="@(StaticWebAsset)" Exclude="@(_NonLinkedStaticWebAssets)" /> - - <_UpdatedStaticWebAssets Include="%(Identity)"> - @(_LinkedStaticWebAssets->'%(SourceType)') - @(_LinkedStaticWebAssets->'%(SourceId)') - @(_LinkedStaticWebAssets->'%(ContentRoot)') - @(_LinkedStaticWebAssets->'%(BasePath)') - @(_WwwrootLinkedContent->'%(RelativePath)') - - - - - - - - - $(PackageId) - - $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)\wwwroot\')) - $(StaticWebAssetBasePath) - %(_OutsideContentLinkedIntoWwwroot.RelativePath) - - - <_StaticWebAssetsPublishFalse Include="@(Content->'%(FullPath)')" Condition="'%(Content.CopyToPublishDirectory)' == 'false'" /> - <_StaticWebAssetsLinkOutsideWwwroot Include="@(Content->'%(FullPath)')" Condition="'%(Content.Link)' != '' and !$([System.String]::Copy('%(Content.Link)').Replace('\','/').StartsWith('wwwroot/'))" /> - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/test/BlazorCreateRootDescriptorFileTest.cs b/src/Components/WebAssembly/Build/test/BlazorCreateRootDescriptorFileTest.cs deleted file mode 100644 index 2ad0ecdddd..0000000000 --- a/src/Components/WebAssembly/Build/test/BlazorCreateRootDescriptorFileTest.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using System.Xml.Linq; -using Xunit; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class BlazorCreateRootDescriptorFileTest - { - [Fact] - public void ProducesRootDescriptor() - { - // Arrange/Act - using var stream = new MemoryStream(); - - // Act - BlazorCreateRootDescriptorFile.WriteRootDescriptor( - stream, - new[] { "MyApp.dll" }); - - // Assert - stream.Position = 0; - var document = XDocument.Load(stream); - var rootElement = document.Root; - - var assemblyElement = Assert.Single(rootElement.Elements()); - Assert.Equal("assembly", assemblyElement.Name.ToString()); - Assert.Equal("MyApp.dll", assemblyElement.Attribute("fullname").Value); - - var typeElement = Assert.Single(assemblyElement.Elements()); - Assert.Equal("type", typeElement.Name.ToString()); - Assert.Equal("*", typeElement.Attribute("fullname").Value); - Assert.Equal("true", typeElement.Attribute("required").Value); - } - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/Assert.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/Assert.cs deleted file mode 100644 index aaf3e508b5..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/Assert.cs +++ /dev/null @@ -1,1006 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - internal class Assert : Xunit.Assert - { - // Matches `{filename}: error {code}: {message} [{project}] - // See https://stackoverflow.com/questions/3441452/msbuild-and-ignorestandarderrorwarningformat/5180353#5180353 - private static readonly Regex ErrorRegex = new Regex(@"^(?'location'.+): error (?'errorcode'[A-Z0-9]+): (?'message'.+) \[(?'project'.+)\]$"); - private static readonly Regex WarningRegex = new Regex(@"^(?'location'.+): warning (?'errorcode'[A-Z0-9]+): (?'message'.+) \[(?'project'.+)\]$"); - private static readonly string[] AllowedBuildWarnings = new[] - { - "MSB3491" , // The process cannot access the file. As long as the build succeeds, we're ok. - "NETSDK1071", // "A PackageReference to 'Microsoft.NETCore.App' specified a Version ..." - }; - - public static void BuildPassed(MSBuildResult result, bool allowWarnings = false) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - if (result.ExitCode != 0) - { - throw new BuildFailedException(result); - } - - var buildWarnings = GetBuildWarnings(result) - .Where(m => !AllowedBuildWarnings.Contains(m.match.Groups["errorcode"].Value)) - .Select(m => m.line); - - if (!allowWarnings && buildWarnings.Any()) - { - throw new BuildWarningsException(result, buildWarnings); - } - } - - public static void BuildError(MSBuildResult result, string errorCode, string location = null) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug. - var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - for (var i = 0; i < lines.Length; i++) - { - var line = lines[i]; - var match = ErrorRegex.Match(line); - if (match.Success) - { - if (match.Groups["errorcode"].Value != errorCode) - { - continue; - } - - if (location != null && match.Groups["location"].Value.Trim() != location) - { - continue; - } - - // This is a match - return; - } - } - - throw new BuildErrorMissingException(result, errorCode, location); - } - - public static void BuildWarning(MSBuildResult result, string errorCode, string location = null) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug. - foreach (var (_, match) in GetBuildWarnings(result)) - { - if (match.Groups["errorcode"].Value != errorCode) - { - continue; - } - - if (location != null && match.Groups["location"].Value.Trim() != location) - { - continue; - } - - // This is a match - return; - } - - throw new BuildErrorMissingException(result, errorCode, location); - } - - private static IEnumerable<(string line, Match match)> GetBuildWarnings(MSBuildResult result) - { - var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - for (var i = 0; i < lines.Length; i++) - { - var line = lines[i]; - var match = WarningRegex.Match(line); - if (match.Success) - { - yield return (line, match); - } - } - } - - public static void BuildFailed(MSBuildResult result) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - }; - - if (result.ExitCode == 0) - { - throw new BuildPassedException(result); - } - } - - public static void BuildOutputContainsLine(MSBuildResult result, string match) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - if (match == null) - { - throw new ArgumentNullException(nameof(match)); - } - - // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug. - var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - for (var i = 0; i < lines.Length; i++) - { - var line = lines[i].Trim(); - if (line == match) - { - return; - } - } - - throw new BuildOutputMissingException(result, match); - } - - public static void BuildOutputDoesNotContainLine(MSBuildResult result, string match) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - if (match == null) - { - throw new ArgumentNullException(nameof(match)); - } - - // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug. - var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - for (var i = 0; i < lines.Length; i++) - { - var line = lines[i].Trim(); - if (line == match) - { - throw new BuildOutputContainsLineException(result, match); - } - } - } - - public static void FileContains(MSBuildResult result, string filePath, string match) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - filePath = Path.Combine(result.Project.DirectoryPath, filePath); - FileExists(result, filePath); - - var text = File.ReadAllText(filePath); - if (text.Contains(match)) - { - return; - } - - throw new FileContentMissingException(result, filePath, File.ReadAllText(filePath), match); - } - - public static void FileDoesNotContain(MSBuildResult result, string filePath, string match) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - filePath = Path.Combine(result.Project.DirectoryPath, filePath); - FileExists(result, filePath); - - var text = File.ReadAllText(filePath); - if (text.Contains(match)) - { - throw new FileContentFoundException(result, filePath, File.ReadAllText(filePath), match); - } - } - - public static void FileContentEquals(MSBuildResult result, string filePath, string expected) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - filePath = Path.Combine(result.Project.DirectoryPath, filePath); - FileExists(result, filePath); - - var actual = File.ReadAllText(filePath); - if (!actual.Equals(expected, StringComparison.Ordinal)) - { - throw new FileContentNotEqualException(result, filePath, expected, actual); - } - } - - public static void FileHashEquals(MSBuildResult result, string filePath, string expectedSha256Base64) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - filePath = Path.Combine(result.Project.DirectoryPath, filePath); - FileExists(result, filePath); - - var actual = File.ReadAllBytes(filePath); - using var algorithm = SHA256.Create(); - var actualSha256 = algorithm.ComputeHash(actual); - var actualSha256Base64 = Convert.ToBase64String(actualSha256); - Assert.Equal(expectedSha256Base64, actualSha256Base64); - } - - public static void FileEquals(MSBuildResult result, string expected, string actual) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - expected = Path.Combine(result.Project.DirectoryPath, expected); - actual = Path.Combine(result.Project.DirectoryPath, actual); - FileExists(result, expected); - FileExists(result, actual); - - if (!Enumerable.SequenceEqual(File.ReadAllBytes(expected), File.ReadAllBytes(actual))) - { - throw new FilesNotEqualException(result, expected, actual); - } - } - - public static void FileContainsLine(MSBuildResult result, string filePath, string match) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - filePath = Path.Combine(result.Project.DirectoryPath, filePath); - FileExists(result, filePath); - - var lines = File.ReadAllLines(filePath); - for (var i = 0; i < lines.Length; i++) - { - var line = lines[i].Trim(); - if (line == match) - { - return; - } - } - - throw new FileContentMissingException(result, filePath, File.ReadAllText(filePath), match); - } - - public static void FileDoesNotContainLine(MSBuildResult result, string filePath, string match) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - filePath = Path.Combine(result.Project.DirectoryPath, filePath); - FileExists(result, filePath); - - var lines = File.ReadAllLines(filePath); - for (var i = 0; i < lines.Length; i++) - { - var line = lines[i].Trim(); - if (line == match) - { - throw new FileContentFoundException(result, filePath, File.ReadAllText(filePath), match); - } - } - } - - public static string FileExists(MSBuildResult result, params string[] paths) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - var filePath = Path.Combine(result.Project.DirectoryPath, Path.Combine(paths)); - if (!File.Exists(filePath)) - { - throw new FileMissingException(result, filePath); - } - - return filePath; - } - - public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - if (directoryPath == null) - { - throw new ArgumentNullException(nameof(directoryPath)); - } - - if (searchPattern == null) - { - throw new ArgumentNullException(nameof(searchPattern)); - } - - directoryPath = Path.Combine(result.Project.DirectoryPath, directoryPath); - - if (Directory.Exists(directoryPath)) - { - var files = Directory.GetFiles(directoryPath, searchPattern, searchOption); - if (files.Length != expected) - { - throw new FileCountException(result, expected, directoryPath, searchPattern, files); - } - } - else if (expected > 0) - { - // directory doesn't exist, that's OK if we expected to find nothing. - throw new FileCountException(result, expected, directoryPath, searchPattern, Array.Empty()); - } - } - - public static void FileDoesNotExist(MSBuildResult result, params string[] paths) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - var filePath = Path.Combine(result.Project.DirectoryPath, Path.Combine(paths)); - if (File.Exists(filePath)) - { - throw new FileFoundException(result, filePath); - } - } - - public static void NuspecContains(MSBuildResult result, string nuspecPath, string expected) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - if (nuspecPath == null) - { - throw new ArgumentNullException(nameof(nuspecPath)); - } - - if (expected == null) - { - throw new ArgumentNullException(nameof(expected)); - } - - nuspecPath = Path.Combine(result.Project.DirectoryPath, nuspecPath); - FileExists(result, nuspecPath); - - var content = File.ReadAllText(nuspecPath); - if (!content.Contains(expected)) - { - throw new NuspecException(result, nuspecPath, content, expected); - } - } - - public static void NuspecDoesNotContain(MSBuildResult result, string nuspecPath, string expected) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - if (nuspecPath == null) - { - throw new ArgumentNullException(nameof(nuspecPath)); - } - - if (expected == null) - { - throw new ArgumentNullException(nameof(expected)); - } - - nuspecPath = Path.Combine(result.Project.DirectoryPath, nuspecPath); - FileExists(result, nuspecPath); - - var content = File.ReadAllText(nuspecPath); - if (content.Contains(expected)) - { - throw new NuspecFoundException(result, nuspecPath, content, expected); - } - } - - // This method extracts the nupkg to a fixed directory path. To avoid the extra work of - // cleaning up after each invocation, this method accepts multiple files. - public static void NupkgContains(MSBuildResult result, string nupkgPath, params string[] filePaths) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - if (nupkgPath == null) - { - throw new ArgumentNullException(nameof(nupkgPath)); - } - - if (filePaths == null) - { - throw new ArgumentNullException(nameof(filePaths)); - } - - nupkgPath = Path.Combine(result.Project.DirectoryPath, nupkgPath); - FileExists(result, nupkgPath); - - var unzipped = Path.Combine(result.Project.DirectoryPath, Path.GetFileNameWithoutExtension(nupkgPath)); - ZipFile.ExtractToDirectory(nupkgPath, unzipped); - - foreach (var filePath in filePaths) - { - if (!File.Exists(Path.Combine(unzipped, filePath))) - { - throw new NupkgFileMissingException(result, nupkgPath, filePath); - } - } - } - - // This method extracts the nupkg to a fixed directory path. To avoid the extra work of - // cleaning up after each invocation, this method accepts multiple files. - public static void NupkgDoesNotContain(MSBuildResult result, string nupkgPath, params string[] filePaths) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - if (nupkgPath == null) - { - throw new ArgumentNullException(nameof(nupkgPath)); - } - - if (filePaths == null) - { - throw new ArgumentNullException(nameof(filePaths)); - } - - nupkgPath = Path.Combine(result.Project.DirectoryPath, nupkgPath); - FileExists(result, nupkgPath); - - var unzipped = Path.Combine(result.Project.DirectoryPath, Path.GetFileNameWithoutExtension(nupkgPath)); - ZipFile.ExtractToDirectory(nupkgPath, unzipped); - - foreach (var filePath in filePaths) - { - if (File.Exists(Path.Combine(unzipped, filePath))) - { - throw new NupkgFileFoundException(result, nupkgPath, filePath); - } - } - } - - public static void AssemblyContainsType(MSBuildResult result, string assemblyPath, string fullTypeName) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); - - var typeNames = GetDeclaredTypeNames(assemblyPath); - Assert.Contains(fullTypeName, typeNames); - } - - public static void AssemblyDoesNotContainType(MSBuildResult result, string assemblyPath, string fullTypeName) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); - - var typeNames = GetDeclaredTypeNames(assemblyPath); - Assert.DoesNotContain(fullTypeName, typeNames); - } - - private static IEnumerable GetDeclaredTypeNames(string assemblyPath) - { - using (var file = File.OpenRead(assemblyPath)) - { - using var peReader = new PEReader(file); - var metadataReader = peReader.GetMetadataReader(); - return metadataReader.TypeDefinitions.Where(t => !t.IsNil).Select(t => - { - var type = metadataReader.GetTypeDefinition(t); - return metadataReader.GetString(type.Namespace) + "." + metadataReader.GetString(type.Name); - }).ToArray(); - } - } - - public static void AssemblyContainsResource(MSBuildResult result, string assemblyPath, string resourceName) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); - - var resources = GetAssemblyResourceNames(assemblyPath); - Assert.Contains(resourceName, resources); - } - - public static void AssemblyDoesNotContainResource(MSBuildResult result, string assemblyPath, string resourceName) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); - - var resources = GetAssemblyResourceNames(assemblyPath); - Assert.DoesNotContain(resourceName, resources); - } - - private static IEnumerable GetAssemblyResourceNames(string assemblyPath) - { - using var file = File.OpenRead(assemblyPath); - using var peReader = new PEReader(file); - var metadataReader = peReader.GetMetadataReader(); - return metadataReader.ManifestResources.Where(r => !r.IsNil).Select(r => - { - var resource = metadataReader.GetManifestResource(r); - return metadataReader.GetString(resource.Name); - }).ToArray(); - } - - public static void AssemblyHasAttribute(MSBuildResult result, string assemblyPath, string fullTypeName) - { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); - - var typeNames = GetAssemblyAttributes(assemblyPath); - Assert.Contains(fullTypeName, typeNames); - } - - private static IEnumerable GetAssemblyAttributes(string assemblyPath) - { - using (var file = File.OpenRead(assemblyPath)) - { - var peReader = new PEReader(file); - var metadataReader = peReader.GetMetadataReader(); - return metadataReader.CustomAttributes.Where(t => !t.IsNil).Select(t => - { - var attribute = metadataReader.GetCustomAttribute(t); - var constructor = metadataReader.GetMemberReference((MemberReferenceHandle)attribute.Constructor); - var type = metadataReader.GetTypeReference((TypeReferenceHandle)constructor.Parent); - - return metadataReader.GetString(type.Namespace) + "." + metadataReader.GetString(type.Name); - }).ToArray(); - } - } - - private abstract class MSBuildXunitException : Xunit.Sdk.XunitException - { - protected MSBuildXunitException(MSBuildResult result) - { - Result = result; - } - - protected abstract string Heading { get; } - - public MSBuildResult Result { get; } - - public override string Message - { - get - { - var message = new StringBuilder(); - message.AppendLine(Heading); - message.Append(Result.FileName); - message.Append(" "); - message.Append(Result.Arguments); - message.AppendLine(); - message.AppendLine(); - message.Append(Result.Output); - message.AppendLine(); - message.Append("Exit Code:"); - message.Append(Result.ExitCode); - return message.ToString(); - } - } - } - - private class BuildErrorMissingException : MSBuildXunitException - { - public BuildErrorMissingException(MSBuildResult result, string errorCode, string location) - : base(result) - { - ErrorCode = errorCode; - Location = location; - } - - public string ErrorCode { get; } - - public string Location { get; } - - protected override string Heading - { - get - { - return - $"Error code '{ErrorCode}' was not found." + Environment.NewLine + - $"Looking for '{Location ?? ".*"}: error {ErrorCode}: .*'"; - } - } - } - - private class BuildFailedException : MSBuildXunitException - { - public BuildFailedException(MSBuildResult result) - : base(result) - { - } - - protected override string Heading => "Build failed."; - } - - private class BuildWarningsException : MSBuildXunitException - { - public BuildWarningsException(MSBuildResult result, IEnumerable warnings) - : base(result) - { - Warnings = warnings.ToList(); - } - - public List Warnings { get; } - - protected override string Heading => "Build contains unexpected warnings: " + string.Join(Environment.NewLine, Warnings); - } - - private class BuildPassedException : MSBuildXunitException - { - public BuildPassedException(MSBuildResult result) - : base(result) - { - } - - protected override string Heading => "Build should have failed, but it passed."; - } - - private class BuildOutputMissingException : MSBuildXunitException - { - public BuildOutputMissingException(MSBuildResult result, string match) - : base(result) - { - Match = match; - } - - public string Match { get; } - - protected override string Heading => $"Build did not contain the line: '{Match}'."; - } - - private class BuildOutputContainsLineException : MSBuildXunitException - { - public BuildOutputContainsLineException(MSBuildResult result, string match) - : base(result) - { - Match = match; - } - - public string Match { get; } - - protected override string Heading => $"Build output contains the line: '{Match}'."; - } - - private class FileContentFoundException : MSBuildXunitException - { - public FileContentFoundException(MSBuildResult result, string filePath, string content, string match) - : base(result) - { - FilePath = filePath; - Content = content; - Match = match; - } - - public string Content { get; } - - public string FilePath { get; } - - public string Match { get; } - - protected override string Heading - { - get - { - var builder = new StringBuilder(); - builder.AppendFormat("File content of '{0}' should not contain line: '{1}'.", FilePath, Match); - builder.AppendLine(); - builder.AppendLine(); - builder.AppendLine(Content); - return builder.ToString(); - } - } - } - - private class FileContentMissingException : MSBuildXunitException - { - public FileContentMissingException(MSBuildResult result, string filePath, string content, string match) - : base(result) - { - FilePath = filePath; - Content = content; - Match = match; - } - - public string Content { get; } - - public string FilePath { get; } - - public string Match { get; } - - protected override string Heading - { - get - { - var builder = new StringBuilder(); - builder.AppendFormat("File content of '{0}' did not contain the line: '{1}'.", FilePath, Match); - builder.AppendLine(); - builder.AppendLine(); - builder.AppendLine(Content); - return builder.ToString(); - } - } - } - - private class FileContentNotEqualException : MSBuildXunitException - { - public FileContentNotEqualException(MSBuildResult result, string filePath, string expected, string actual) - : base(result) - { - FilePath = filePath; - Expected = expected; - Actual = actual; - } - - public string Actual { get; } - - public string FilePath { get; } - - public string Expected { get; } - - protected override string Heading - { - get - { - var builder = new StringBuilder(); - builder.AppendFormat("File content of '{0}' did not match the expected content: '{1}'.", FilePath, Expected); - builder.AppendLine(); - builder.AppendLine(); - builder.AppendLine(Actual); - return builder.ToString(); - } - } - } - - private class FilesNotEqualException : MSBuildXunitException - { - public FilesNotEqualException(MSBuildResult result, string expected, string actual) - : base(result) - { - Expected = expected; - Actual = actual; - } - - public string Actual { get; } - - public string Expected { get; } - - protected override string Heading - { - get - { - var builder = new StringBuilder(); - builder.AppendFormat("File content of '{0}' did not match the contents of '{1}'.", Expected, Actual); - builder.AppendLine(); - builder.AppendLine(); - builder.AppendLine(Actual); - return builder.ToString(); - } - } - } - - private class FileMissingException : MSBuildXunitException - { - public FileMissingException(MSBuildResult result, string filePath) - : base(result) - { - FilePath = filePath; - } - - public string FilePath { get; } - - protected override string Heading => $"File: '{FilePath}' was not found."; - } - - private class FileCountException : MSBuildXunitException - { - public FileCountException(MSBuildResult result, int expected, string directoryPath, string searchPattern, string[] files) - : base(result) - { - Expected = expected; - DirectoryPath = directoryPath; - SearchPattern = searchPattern; - Files = files; - } - - public string DirectoryPath { get; } - - public int Expected { get; } - - public string[] Files { get; } - - public string SearchPattern { get; } - - protected override string Heading - { - get - { - var heading = new StringBuilder(); - heading.AppendLine($"Expected {Expected} files matching {SearchPattern} in {DirectoryPath}, found {Files.Length}."); - - if (Files.Length > 0) - { - heading.AppendLine("Files:"); - - foreach (var file in Files) - { - heading.Append("\t"); - heading.Append(file); - } - - heading.AppendLine(); - } - - return heading.ToString(); - } - } - } - - private class FileFoundException : MSBuildXunitException - { - public FileFoundException(MSBuildResult result, string filePath) - : base(result) - { - FilePath = filePath; - } - - public string FilePath { get; } - - protected override string Heading => $"File: '{FilePath}' was found, but should not exist."; - } - - private class NuspecException : MSBuildXunitException - { - public NuspecException(MSBuildResult result, string filePath, string content, string expected) - : base(result) - { - FilePath = filePath; - Content = content; - Expected = expected; - } - - public string Content { get; } - - public string Expected { get; } - - public string FilePath { get; } - - protected override string Heading - { - get - { - return - $"nuspec: '{FilePath}' did not contain the expected content." + Environment.NewLine + - Environment.NewLine + - $"expected: {Expected}" + Environment.NewLine + - Environment.NewLine + - $"actual: {Content}"; - } - } - } - - private class NuspecFoundException : MSBuildXunitException - { - public NuspecFoundException(MSBuildResult result, string filePath, string content, string expected) - : base(result) - { - FilePath = filePath; - Content = content; - Expected = expected; - } - - public string Content { get; } - - public string Expected { get; } - - public string FilePath { get; } - - protected override string Heading - { - get - { - return - $"nuspec: '{FilePath}' should not contain the content {Expected}." + - Environment.NewLine + - $"actual content: {Content}"; - } - } - } - - private class NupkgFileMissingException : MSBuildXunitException - { - public NupkgFileMissingException(MSBuildResult result, string nupkgPath, string filePath) - : base(result) - { - NupkgPath = nupkgPath; - FilePath = filePath; - } - - public string FilePath { get; } - - public string NupkgPath { get; } - - protected override string Heading => $"File: '{FilePath}' was not found was not found in {NupkgPath}."; - } - - private class NupkgFileFoundException : MSBuildXunitException - { - public NupkgFileFoundException(MSBuildResult result, string nupkgPath, string filePath) - : base(result) - { - NupkgPath = nupkgPath; - FilePath = filePath; - } - - public string FilePath { get; } - - public string NupkgPath { get; } - - protected override string Heading => $"File: '{FilePath}' was found in {NupkgPath}."; - } - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildCompressionTests.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildCompressionTests.cs deleted file mode 100644 index 397d32bd61..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildCompressionTests.cs +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class BuildCompressionTests - { - [Fact] - public async Task Build_WithLinkerAndCompression_IsIncremental() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - // Act - var compressedFilesFolder = Path.Combine(project.IntermediateOutputDirectory, "compressed"); - var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); - - // Assert - for (var i = 0; i < 3; i++) - { - result = await MSBuildProcessManager.DotnetMSBuild(project); - Assert.BuildPassed(result); - - var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); - Assert.Equal(thumbPrint.Count, newThumbPrint.Count); - for (var j = 0; j < thumbPrint.Count; j++) - { - Assert.Equal(thumbPrint[j], newThumbPrint[j]); - } - } - } - - [Fact] - public async Task Build_WithLinkerAndCompression_UpdatesFilesWhenSourcesChange() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var mainAppDll = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); - var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll.gz"); - var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); - - var blazorBootJson = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - var blazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); - var blazorBootJsonCompressed = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json.gz"); - var blazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); - - // Act - var programFile = Path.Combine(project.DirectoryPath, "Program.cs"); - var programFileContents = File.ReadAllText(programFile); - File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); - result = await MSBuildProcessManager.DotnetMSBuild(project); - - // Assert - Assert.BuildPassed(result); - var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); - var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); - var newBlazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); - var newBlazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); - - Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); - Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); - - Assert.NotEqual(blazorBootJsonThumbPrint, newBlazorBootJsonThumbPrint); - Assert.NotEqual(blazorBootJsonCompressedThumbPrint, newBlazorBootJsonCompressedThumbPrint); - } - - [Fact] - public async Task Build_WithoutLinkerAndCompression_IsIncremental() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false"); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - // Act - var compressedFilesFolder = Path.Combine(project.IntermediateOutputDirectory, "compressed"); - var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); - - // Assert - for (var i = 0; i < 3; i++) - { - result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false"); - Assert.BuildPassed(result); - - var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); - Assert.Equal(thumbPrint.Count, newThumbPrint.Count); - for (var j = 0; j < thumbPrint.Count; j++) - { - Assert.Equal(thumbPrint[j], newThumbPrint[j]); - } - } - } - - [Fact] - public async Task Build_WithoutLinkerAndCompression_UpdatesFilesWhenSourcesChange() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false"); - - // Act - var mainAppDll = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); - - var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll.gz"); - var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); - - var programFile = Path.Combine(project.DirectoryPath, "Program.cs"); - var programFileContents = File.ReadAllText(programFile); - File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); - - // Assert - result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false"); - Assert.BuildPassed(result); - var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); - var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); - - Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); - Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); - } - - [Fact] - public async Task Build_CompressesAllFrameworkFiles() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - var extensions = new[] { ".dll", ".js", ".pdb", ".wasm", ".map", ".json", ".dat" }; - // Act - var compressedFilesPath = Path.Combine( - project.DirectoryPath, - project.IntermediateOutputDirectory, - "compressed", - "_framework"); - var compressedFiles = Directory.EnumerateFiles( - compressedFilesPath, - "*", - SearchOption.AllDirectories) - .Where(f => Path.GetExtension(f) == ".gz") - .Select(f => Path.GetRelativePath(compressedFilesPath, f[0..^3])) - .OrderBy(f => f) - .ToArray(); - - var frameworkFilesPath = Path.Combine( - project.DirectoryPath, - project.BuildOutputDirectory, - "wwwroot", - "_framework"); - var frameworkFiles = Directory.EnumerateFiles( - frameworkFilesPath, - "*", - SearchOption.AllDirectories) - .Where(f => extensions.Contains(Path.GetExtension(f))) - .Select(f => Path.GetRelativePath(frameworkFilesPath, f)) - .OrderBy(f => f) - .ToArray(); - - Assert.Equal(frameworkFiles.Length, compressedFiles.Length); - Assert.Equal(frameworkFiles, compressedFiles); - - var brotliFiles = Directory.EnumerateFiles( - compressedFilesPath, - "*", - SearchOption.AllDirectories) - .Where(f => Path.GetExtension(f) == ".br") - .Select(f => Path.GetRelativePath(compressedFilesPath, f[0..^3])) - .OrderBy(f => f) - .ToArray(); - - // We don't compress things with brotli at build time - Assert.Empty(brotliFiles); - } - - [Fact] - public async Task Build_DisabledCompression_DoesNotCompressFiles() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - - // Act - var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorEnableCompression=false"); - - //Assert - Assert.BuildPassed(result); - - var compressedFilesPath = Path.Combine( - project.DirectoryPath, - project.IntermediateOutputDirectory, - "compressed"); - - Assert.False(Directory.Exists(compressedFilesPath)); - } - - [Fact] - public async Task Publish_WithLinkerAndCompression_UpdatesFilesWhenSourcesChange() - { - // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary" }); - project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework; - var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); - - Assert.BuildPassed(result); - - // Act - var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); - var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll.br"); - var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); - - var blazorBootJson = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - var blazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); - var blazorBootJsonCompressed = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json.br"); - var blazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); - - var programFile = Path.Combine(project.DirectoryPath, "..", "standalone", "Program.cs"); - var programFileContents = File.ReadAllText(programFile); - File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); - - // Assert - result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); - Assert.BuildPassed(result); - var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); - var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); - var newBlazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); - var newBlazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); - - Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); - Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); - - Assert.NotEqual(blazorBootJsonThumbPrint, newBlazorBootJsonThumbPrint); - Assert.NotEqual(blazorBootJsonCompressedThumbPrint, newBlazorBootJsonCompressedThumbPrint); - } - - [Fact] - public async Task Publish_WithoutLinkerAndCompression_UpdatesFilesWhenSourcesChange() - { - // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary" }); - project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework; - var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false"); - - Assert.BuildPassed(result); - - // Act - var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); - - var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll.br"); - var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); - - var programFile = Path.Combine(project.DirectoryPath, "..", "standalone", "Program.cs"); - var programFileContents = File.ReadAllText(programFile); - File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); - - // Assert - result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false"); - Assert.BuildPassed(result); - var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); - var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); - - Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); - Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); - } - - [Fact] - public async Task Publish_WithLinkerAndCompression_IsIncremental() - { - // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - // Act - var compressedFilesFolder = Path.Combine("..", "standalone", project.IntermediateOutputDirectory, "compressed"); - var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); - - // Assert - for (var i = 0; i < 3; i++) - { - result = await MSBuildProcessManager.DotnetMSBuild(project); - Assert.BuildPassed(result); - - var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); - Assert.Equal(thumbPrint.Count, newThumbPrint.Count); - for (var j = 0; j < thumbPrint.Count; j++) - { - Assert.Equal(thumbPrint[j], newThumbPrint[j]); - } - } - } - - [Fact] - public async Task Publish_WithoutLinkerAndCompression_IsIncremental() - { - // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false"); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - // Act - var compressedFilesFolder = Path.Combine("..", "standalone", project.IntermediateOutputDirectory, "compressed"); - var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); - - // Assert - for (var i = 0; i < 3; i++) - { - result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false"); - Assert.BuildPassed(result); - - var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); - Assert.Equal(thumbPrint.Count, newThumbPrint.Count); - for (var j = 0; j < thumbPrint.Count; j++) - { - Assert.Equal(thumbPrint[j], newThumbPrint[j]); - } - } - } - - [Fact] - public async Task Publish_CompressesAllFrameworkFiles() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - var extensions = new[] { ".dll", ".js", ".pdb", ".wasm", ".map", ".json", ".dat" }; - // Act - var compressedFilesPath = Path.Combine( - project.DirectoryPath, - "..", - "standalone", - project.IntermediateOutputDirectory, - "compressed", - "_framework"); - var compressedFiles = Directory.EnumerateFiles( - compressedFilesPath, - "*", - SearchOption.AllDirectories) - .Where(f => Path.GetExtension(f) == ".br") - .Select(f => Path.GetRelativePath(compressedFilesPath, f[0..^3])) - .OrderBy(f => f) - .ToArray(); - - var frameworkFilesPath = Path.Combine( - project.DirectoryPath, - project.BuildOutputDirectory, - "wwwroot", - "_framework"); - var frameworkFiles = Directory.EnumerateFiles( - frameworkFilesPath, - "*", - SearchOption.AllDirectories) - .Where(f => extensions.Contains(Path.GetExtension(f))) - .Select(f => Path.GetRelativePath(frameworkFilesPath, f)) - .OrderBy(f => f) - .ToArray(); - - Assert.Equal(frameworkFiles.Length, compressedFiles.Length); - Assert.Equal(frameworkFiles, compressedFiles); - } - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs deleted file mode 100644 index c9184b649d..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using System.Text.Json; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Testing; -using Xunit; -using static Microsoft.AspNetCore.Components.WebAssembly.Build.WebAssemblyRuntimePackage; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class BuildIntegrationTest - { - [Fact] - public async Task Build_WithDefaultSettings_Works() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.Configuration = "Debug"; - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.timezones.dat"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.pdb"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.pdb"); - - var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "standalone.StaticWebAssets.xml"); - Assert.FileContains(result, staticWebAssets, Path.Combine("netstandard2.1", "wwwroot")); - } - - [Fact] - public async Task Build_InRelease_Works() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.Configuration = "Release"; - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.timezones.dat"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. - Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.pdb"); - Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.pdb"); - - var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "standalone.StaticWebAssets.xml"); - Assert.FileContains(result, staticWebAssets, Path.Combine("netstandard2.1", "wwwroot")); - } - - [Fact] - public async Task Build_ProducesBootJsonDataWithExpectedContent() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.Configuration = "Debug"; - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - var bootJsonData = ReadBootJsonData(result, bootJsonPath); - - var runtime = bootJsonData.resources.runtime.Keys; - Assert.Contains(DotNetJsFileName, runtime); - Assert.Contains("dotnet.wasm", runtime); - Assert.Contains("dotnet.timezones.dat", runtime); - - var assemblies = bootJsonData.resources.assembly.Keys; - Assert.Contains("standalone.dll", assemblies); - Assert.Contains("RazorClassLibrary.dll", assemblies); - Assert.Contains("Microsoft.Extensions.Logging.Abstractions.dll", assemblies); - - var pdb = bootJsonData.resources.pdb.Keys; - Assert.Contains("standalone.pdb", pdb); - Assert.Contains("RazorClassLibrary.pdb", pdb); - - Assert.Null(bootJsonData.resources.satelliteResources); - } - - [Fact] - public async Task Build_InRelease_ProducesBootJsonDataWithExpectedContent() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.Configuration = "Release"; - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - var bootJsonData = ReadBootJsonData(result, bootJsonPath); - - var runtime = bootJsonData.resources.runtime.Keys; - Assert.Contains(DotNetJsFileName, runtime); - Assert.Contains("dotnet.wasm", runtime); - Assert.Contains("dotnet.timezones.dat", runtime); - - var assemblies = bootJsonData.resources.assembly.Keys; - Assert.Contains("standalone.dll", assemblies); - Assert.Contains("RazorClassLibrary.dll", assemblies); - Assert.Contains("Microsoft.Extensions.Logging.Abstractions.dll", assemblies); - - Assert.Null(bootJsonData.resources.pdb); - Assert.Null(bootJsonData.resources.satelliteResources); - } - - [Fact] - public async Task Build_WithBlazorEnableTimeZoneSupportDisabled_DoesNotCopyTimeZoneInfo() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.Configuration = "Release"; - project.AddProjectFileContent( -@" - - false -"); - - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - var bootJsonData = ReadBootJsonData(result, bootJsonPath); - - var runtime = bootJsonData.resources.runtime.Keys; - Assert.Contains("dotnet.wasm", runtime); - Assert.DoesNotContain("dotnet.timezones.dat", runtime); - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm"); - Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.timezones.dat"); - } - - [Fact] - public async Task Build_Hosted_Works() - { - // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", }); - project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework; - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - var path = Path.GetFullPath(Path.Combine(project.SolutionPath, "standalone", "bin", project.Configuration, "netstandard2.1", "standalone.dll")); - Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - - var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorhosted.StaticWebAssets.xml"); - Assert.FileContains(result, staticWebAssets, Path.Combine("netstandard2.1", "wwwroot")); - Assert.FileContains(result, staticWebAssets, Path.Combine("razorclasslibrary", "wwwroot")); - Assert.FileContains(result, staticWebAssets, Path.Combine("standalone", "wwwroot")); - } - - [Fact] - public async Task Build_WithLinkOnBuildDisabled_Works() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.AddProjectFileContent( -@" - false -"); - - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.timezones.dat"); - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. - } - - [Fact] - [QuarantinedTest] - public async Task Build_SatelliteAssembliesAreCopiedToBuildOutput() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" }); - project.AddProjectFileContent( -@" - - $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies - - - -"); - var resxfileInProject = Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt"); - File.Move(resxfileInProject, Path.Combine(project.DirectoryPath, "Resource.ja.resx")); - - var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore"); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "classlibrarywithsatelliteassemblies.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.CodeAnalysis.CSharp.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output. - - var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\""); - Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\""); - } - - [Fact] - public async Task Build_WithBlazorWebAssemblyEnableLinkingFalse_SatelliteAssembliesAreCopiedToBuildOutput() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" }); - project.AddProjectFileContent( -@" - - false - $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies - - - -"); - - var resxfileInProject = Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt"); - File.Move(resxfileInProject, Path.Combine(project.DirectoryPath, "Resource.ja.resx")); - - var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore"); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "classlibrarywithsatelliteassemblies.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.CodeAnalysis.CSharp.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output. - - var bootJson = ReadBootJsonData(result, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json")); - - var satelliteResources = bootJson.resources.satelliteResources; - Assert.NotNull(satelliteResources); - - Assert.Contains("es-ES", satelliteResources.Keys); - Assert.Contains("es-ES/classlibrarywithsatelliteassemblies.resources.dll", satelliteResources["es-ES"].Keys); - Assert.Contains("fr", satelliteResources.Keys); - Assert.Contains("fr/Microsoft.CodeAnalysis.CSharp.resources.dll", satelliteResources["fr"].Keys); - Assert.Contains("ja", satelliteResources.Keys); - Assert.Contains("ja/standalone.resources.dll", satelliteResources["ja"].Keys); - } - - - [Fact] - public async Task Build_WithI8NOption_CopiesI8NAssembliesWithoutLinkerEnabled() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.Configuration = "Debug"; - project.AddProjectFileContent( -@" - - other -"); - - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "I18N.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "I18N.Other.dll"); - // When running without linker, we copy all I18N assemblies. Look for one additional - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "I18N.West.dll"); - } - - [Fact] - public async Task Build_WithI8NOption_CopiesI8NAssembliesWithLinkerEnabled() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.Configuration = "Debug"; - project.AddProjectFileContent( -@" - - true - other -"); - - var result = await MSBuildProcessManager.DotnetMSBuild(project); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "I18N.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "I18N.Other.dll"); - Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "I18N.West.dll"); - } - - [Fact] - public async Task Build_WithCustomOutputPath_Works() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - - project.AddDirectoryBuildContent( -@" - $(MSBuildThisFileDirectory)build\bin\ - $(MSBuildThisFileDirectory)build\obj\ -"); - - var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore"); - Assert.BuildPassed(result); - - var compressedFilesPath = Path.Combine( - project.DirectoryPath, - "build", - project.IntermediateOutputDirectory, - "compressed"); - - Assert.True(Directory.Exists(compressedFilesPath)); - } - - private static GenerateBlazorBootJson.BootJsonData ReadBootJsonData(MSBuildResult result, string path) - { - return JsonSerializer.Deserialize( - File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)), - new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - } - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/FileThumbPrint.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/FileThumbPrint.cs deleted file mode 100644 index c5f210be52..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/FileThumbPrint.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - internal class FileThumbPrint : IEquatable - { - private FileThumbPrint(string path, DateTime lastWriteTimeUtc, string hash) - { - FilePath = path; - LastWriteTimeUtc = lastWriteTimeUtc; - Hash = hash; - } - - public string FilePath { get; } - - public DateTime LastWriteTimeUtc { get; } - - public string Hash { get; } - - public override string ToString() - { - return $"{Path.GetFileName(FilePath)}, {LastWriteTimeUtc.ToString("u")}, {Hash}"; - } - - /// - /// Returns a list of thumbprints for all files (recursive) in the specified directory, sorted by file paths. - /// - public static List CreateFolderThumbprint(ProjectDirectory project, string directoryPath, params string[] filesToIgnore) - { - directoryPath = Path.Combine(project.DirectoryPath, directoryPath); - var files = Directory.GetFiles(directoryPath).Where(p => !filesToIgnore.Contains(p)); - var thumbprintLookup = new List(); - foreach (var file in files) - { - var thumbprint = Create(file); - thumbprintLookup.Add(thumbprint); - } - - thumbprintLookup.Sort(Comparer.Create((a, b) => StringComparer.Ordinal.Compare(a.FilePath, b.FilePath))); - return thumbprintLookup; - } - - public static FileThumbPrint Create(string path) - { - byte[] hashBytes; - using (var sha1 = SHA1.Create()) - using (var fileStream = File.OpenRead(path)) - { - hashBytes = sha1.ComputeHash(fileStream); - } - - var hash = Convert.ToBase64String(hashBytes); - var lastWriteTimeUtc = File.GetLastWriteTimeUtc(path); - return new FileThumbPrint(path, lastWriteTimeUtc, hash); - } - - public bool Equals(FileThumbPrint other) - { - return - string.Equals(FilePath, other.FilePath, StringComparison.Ordinal) && - LastWriteTimeUtc == other.LastWriteTimeUtc && - string.Equals(Hash, other.Hash, StringComparison.Ordinal); - } - - public override int GetHashCode() => LastWriteTimeUtc.GetHashCode(); - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/MSBuildProcessManager.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/MSBuildProcessManager.cs deleted file mode 100644 index e1ee50af43..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/MSBuildProcessManager.cs +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.CommandLineUtils; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - internal static class MSBuildProcessManager - { - public static Task DotnetMSBuild( - ProjectDirectory project, - string target = "Build", - string args = null) - { - var buildArgumentList = new List - { - // Disable node-reuse. We don't want msbuild processes to stick around - // once the test is completed. - "/nr:false", - - // Always generate a bin log for debugging purposes - "/bl", - }; - - buildArgumentList.Add($"/t:{target}"); - buildArgumentList.Add($"/p:Configuration={project.Configuration}"); - buildArgumentList.Add($"/p:BlazorBuildConfiguration={ProjectDirectory.TestProjectConfiguration}"); - buildArgumentList.Add(args); - - var buildArguments = string.Join(" ", buildArgumentList); - - return MSBuildProcessManager.RunProcessAsync(project, buildArguments); - } - - public static async Task RunProcessAsync( - ProjectDirectory project, - string arguments, - TimeSpan? timeout = null) - { - var processStartInfo = new ProcessStartInfo() - { - WorkingDirectory = project.DirectoryPath, - UseShellExecute = false, - RedirectStandardError = true, - RedirectStandardOutput = true, - }; - - processStartInfo.FileName = DotNetMuxer.MuxerPathOrDefault(); - processStartInfo.Arguments = $"msbuild {arguments}"; - - // Suppresses the 'Welcome to .NET Core!' output that times out tests and causes locked file issues. - // When using dotnet we're not guarunteed to run in an environment where the dotnet.exe has had its first run experience already invoked. - processStartInfo.EnvironmentVariables["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "true"; - processStartInfo.EnvironmentVariables["_BlazorWebAssemblyBuildTest_BrotliCompressionLevel_NoCompression"] = "1"; - - var processResult = await RunProcessCoreAsync(processStartInfo, timeout); - - return new MSBuildResult(project, processResult.FileName, processResult.Arguments, processResult.ExitCode, processResult.Output); - } - - internal static Task RunProcessCoreAsync( - ProcessStartInfo processStartInfo, - TimeSpan? timeout = null) - { - timeout = timeout ?? TimeSpan.FromSeconds(5 * 60); - - var process = new Process() - { - StartInfo = processStartInfo, - EnableRaisingEvents = true, - }; - - var output = new StringBuilder(); - var outputLock = new object(); - - var diagnostics = new StringBuilder(); - diagnostics.AppendLine("Process execution diagnostics:"); - - process.ErrorDataReceived += Process_ErrorDataReceived; - process.OutputDataReceived += Process_OutputDataReceived; - - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - - var timeoutTask = GetTimeoutForProcess(process, timeout, diagnostics); - - var waitTask = Task.Run(() => - { - // We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously - // this code used Process.Exited, which could result in us missing some output due to the ordering of - // events. - // - // See the remarks here: https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx - if (!process.WaitForExit(Int32.MaxValue)) - { - // unreachable - the timeoutTask will kill the process before this happens. - throw new TimeoutException(); - } - - process.WaitForExit(); - - - string outputString; - lock (outputLock) - { - // This marks the end of the diagnostic info which we collect when something goes wrong. - diagnostics.AppendLine("Process output:"); - - // Expected output - // Process execution diagnostics: - // ... - // Process output: - outputString = diagnostics.ToString(); - outputString += output.ToString(); - } - - var result = new ProcessResult(process.StartInfo.FileName, process.StartInfo.Arguments, process.ExitCode, outputString); - return result; - }); - - return Task.WhenAny(waitTask, timeoutTask).Unwrap(); - - void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) - { - lock (outputLock) - { - output.AppendLine(e.Data); - } - } - - void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) - { - lock (outputLock) - { - output.AppendLine(e.Data); - } - } - - async Task GetTimeoutForProcess(Process process, TimeSpan? timeout, StringBuilder diagnostics) - { - await Task.Delay(timeout.Value); - - // Don't timeout during debug sessions - while (Debugger.IsAttached) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - } - if (!process.HasExited) - { - await CollectDumps(process, timeout, diagnostics); - - // This is a timeout. - process.Kill(); - } - - throw new TimeoutException($"command '${process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {timeout}. Output: {output.ToString()}"); - } - - static async Task CollectDumps(Process process, TimeSpan? timeout, StringBuilder diagnostics) - { - var procDumpProcess = await CaptureDump(process, timeout, diagnostics); - var allDotNetProcesses = Process.GetProcessesByName("dotnet"); - - var allDotNetChildProcessCandidates = allDotNetProcesses - .Where(p => p.StartTime >= process.StartTime && p.Id != process.Id); - - if (!allDotNetChildProcessCandidates.Any()) - { - diagnostics.AppendLine("Couldn't find any candidate child process."); - foreach (var dotnetProcess in allDotNetProcesses) - { - diagnostics.AppendLine($"Found dotnet process with PID {dotnetProcess.Id} and start time {dotnetProcess.StartTime}."); - } - } - - foreach (var childProcess in allDotNetChildProcessCandidates) - { - diagnostics.AppendLine($"Found child process candidate '{childProcess.Id}'."); - } - - var childrenProcessDumpProcesses = await Task.WhenAll(allDotNetChildProcessCandidates.Select(d => CaptureDump(d, timeout, diagnostics))); - foreach (var childProcess in childrenProcessDumpProcesses) - { - if (childProcess != null && childProcess.HasExited) - { - diagnostics.AppendLine($"ProcDump failed to run for child dotnet process candidate '{process.Id}'."); - childProcess.Kill(); - } - } - - if (procDumpProcess != null && procDumpProcess.HasExited) - { - diagnostics.AppendLine($"ProcDump failed to run for '{process.Id}'."); - procDumpProcess.Kill(); - } - } - - static async Task CaptureDump(Process process, TimeSpan? timeout, StringBuilder diagnostics) - { - var metadataAttributes = Assembly.GetExecutingAssembly() - .GetCustomAttributes(); - - var procDumpPath = metadataAttributes - .SingleOrDefault(ama => ama.Key == "ProcDumpToolPath")?.Value; - - if (string.IsNullOrEmpty(procDumpPath)) - { - diagnostics.AppendLine("ProcDumpPath not defined."); - return null; - } - var procDumpExePath = Path.Combine(procDumpPath, "procdump.exe"); - if (!File.Exists(procDumpExePath)) - { - diagnostics.AppendLine($"Can't find procdump.exe in '{procDumpPath}'."); - return null; - } - - var dumpDirectory = metadataAttributes - .SingleOrDefault(ama => ama.Key == "ArtifactsLogDir")?.Value; - - if (string.IsNullOrEmpty(dumpDirectory)) - { - diagnostics.AppendLine("ArtifactsLogDir not defined."); - return null; - } - - if (!Directory.Exists(dumpDirectory)) - { - diagnostics.AppendLine($"'{dumpDirectory}' does not exist."); - return null; - } - - var procDumpPattern = Path.Combine(dumpDirectory, "HangingProcess_PROCESSNAME_PID_YYMMDD_HHMMSS.dmp"); - var procDumpStartInfo = new ProcessStartInfo( - procDumpExePath, - $"-accepteula -ma {process.Id} {procDumpPattern}") - { - CreateNoWindow = true, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - var procDumpProcess = Process.Start(procDumpStartInfo); - var tcs = new TaskCompletionSource(); - - procDumpProcess.Exited += (s, a) => tcs.TrySetResult(null); - procDumpProcess.EnableRaisingEvents = true; - - await Task.WhenAny(tcs.Task, Task.Delay(timeout ?? TimeSpan.FromSeconds(30))); - return procDumpProcess; - } - } - - internal class ProcessResult - { - public ProcessResult(string fileName, string arguments, int exitCode, string output) - { - FileName = fileName; - Arguments = arguments; - ExitCode = exitCode; - Output = output; - } - - public string Arguments { get; } - - public string FileName { get; } - - public int ExitCode { get; } - - public string Output { get; } - } - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/MSBuildResult.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/MSBuildResult.cs deleted file mode 100644 index 4ad24e269d..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/MSBuildResult.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - internal class MSBuildResult - { - public MSBuildResult(ProjectDirectory project, string fileName, string arguments, int exitCode, string output) - { - Project = project; - FileName = fileName; - Arguments = arguments; - ExitCode = exitCode; - Output = output; - } - - public ProjectDirectory Project { get; } - - public string Arguments { get; } - - public string FileName { get; } - - public int ExitCode { get; } - - public string Output { get; } - } - -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/ProjectDirectory.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/ProjectDirectory.cs deleted file mode 100644 index 97ad1e8bd3..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/ProjectDirectory.cs +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - internal class ProjectDirectory : IDisposable - { - public bool PreserveWorkingDirectory { get; set; } = false; - - // Configuration the test project is building in. - public static readonly string TestProjectConfiguration -#if DEBUG - = "Debug"; -#elif RELEASE - = "Release"; -#else -#error Configuration not supported -#endif - - private static readonly string RepoRoot = GetTestAttribute("Testing.RepoRoot"); - - public static ProjectDirectory Create(string projectName, string baseDirectory = "", string[] additionalProjects = null) - { - var destinationPath = Path.Combine(Path.GetTempPath(), "BlazorBuild", baseDirectory, Path.GetRandomFileName()); - Directory.CreateDirectory(destinationPath); - - try - { - if (Directory.EnumerateFiles(destinationPath).Any()) - { - throw new InvalidOperationException($"{destinationPath} should be empty"); - } - - if (string.IsNullOrEmpty(RepoRoot)) - { - throw new InvalidOperationException("RepoRoot was not specified."); - } - - var testAppsRoot = Path.Combine(RepoRoot, "src", "Components", "WebAssembly", "Build", "testassets"); - foreach (var project in new string[] { projectName, }.Concat(additionalProjects ?? Array.Empty())) - { - var projectRoot = Path.Combine(testAppsRoot, project); - if (!Directory.Exists(projectRoot)) - { - throw new InvalidOperationException($"Could not find project at '{projectRoot}'"); - } - - var projectDestination = Path.Combine(destinationPath, project); - var projectDestinationDir = Directory.CreateDirectory(projectDestination); - CopyDirectory(new DirectoryInfo(projectRoot), projectDestinationDir); - SetupDirectoryBuildFiles(RepoRoot, testAppsRoot, projectDestination); - } - - var directoryPath = Path.Combine(destinationPath, projectName); - var projectPath = Path.Combine(directoryPath, projectName + ".csproj"); - - CopyRepositoryAssets(destinationPath); - - return new ProjectDirectory( - destinationPath, - directoryPath, - projectPath); - } - catch - { - CleanupDirectory(destinationPath); - throw; - } - - static void CopyDirectory(DirectoryInfo source, DirectoryInfo destination, bool recursive = true) - { - foreach (var file in source.EnumerateFiles()) - { - file.CopyTo(Path.Combine(destination.FullName, file.Name)); - } - - if (!recursive) - { - return; - } - - foreach (var directory in source.EnumerateDirectories()) - { - if (directory.Name == "bin") - { - // Just in case someone has opened the project in an IDE or built it. We don't want to copy - // these folders. - continue; - } - - var created = destination.CreateSubdirectory(directory.Name); - if (directory.Name == "obj") - { - // Copy NuGet restore assets (viz all the files at the root of the obj directory, but stop there.) - CopyDirectory(directory, created, recursive: false); - } - else - { - CopyDirectory(directory, created); - } - } - } - - static void SetupDirectoryBuildFiles(string repoRoot, string testAppsRoot, string projectDestination) - { - var razorSdkDirectoryRoot = TestFacts.RazorSdkDirectoryRoot; - var beforeDirectoryPropsContent = -$@" - - {repoRoot} - {razorSdkDirectoryRoot} - -"; - File.WriteAllText(Path.Combine(projectDestination, "Before.Directory.Build.props"), beforeDirectoryPropsContent); - - new List { "Directory.Build.props", "Directory.Build.targets", } - .ForEach(file => - { - var source = Path.Combine(testAppsRoot, file); - var destination = Path.Combine(projectDestination, file); - File.Copy(source, destination); - }); - } - - static void CopyRepositoryAssets(string projectRoot) - { - const string GlobalJsonFileName = "global.json"; - var globalJsonPath = Path.Combine(RepoRoot, GlobalJsonFileName); - - var destinationFile = Path.Combine(projectRoot, GlobalJsonFileName); - File.Copy(globalJsonPath, destinationFile); - } - } - - protected ProjectDirectory(string solutionPath, string directoryPath, string projectFilePath) - { - SolutionPath = solutionPath; - DirectoryPath = directoryPath; - ProjectFilePath = projectFilePath; - } - - public string DirectoryPath { get; } - - public string ProjectFilePath { get; } - - public string SolutionPath { get; } - - public string TargetFramework { get; set; } = "netstandard2.1"; - - public string Configuration { get; set; } = TestProjectConfiguration; - - public string IntermediateOutputDirectory => Path.Combine("obj", Configuration, TargetFramework); - - public string BuildOutputDirectory => Path.Combine("bin", Configuration, TargetFramework); - - public string PublishOutputDirectory => Path.Combine(BuildOutputDirectory, "publish"); - - internal void AddProjectFileContent(string content) - { - if (content == null) - { - throw new ArgumentNullException(nameof(content)); - } - - var existing = File.ReadAllText(ProjectFilePath); - var updated = existing.Replace("", content); - File.WriteAllText(ProjectFilePath, updated); - } - - internal void AddDirectoryBuildContent(string content) - { - if (content == null) - { - throw new ArgumentNullException(nameof(content)); - } - - var filepath = Path.Combine(DirectoryPath, "Directory.Build.props"); - - var existing = File.ReadAllText(filepath); - var updated = existing.Replace("", content); - File.WriteAllText(filepath, updated); - } - - public void Dispose() - { - if (PreserveWorkingDirectory) - { - Console.WriteLine($"Skipping deletion of working directory {SolutionPath}"); - } - else - { - CleanupDirectory(SolutionPath); - } - } - - internal static void CleanupDirectory(string filePath) - { - var tries = 5; - var sleep = TimeSpan.FromSeconds(3); - - for (var i = 0; i < tries; i++) - { - try - { - Directory.Delete(filePath, recursive: true); - return; - } - catch when (i < tries - 1) - { - Console.WriteLine($"Failed to delete directory {filePath}, trying again."); - Thread.Sleep(sleep); - } - } - } - - private static string GetTestAttribute(string key) - { - return typeof(ProjectDirectory).Assembly - .GetCustomAttributes() - .FirstOrDefault(f => f.Key == key) - ?.Value; - } - - public override string ToString() => DirectoryPath; - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/ProjectDirectoryTest.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/ProjectDirectoryTest.cs deleted file mode 100644 index 130a14ef8d..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/ProjectDirectoryTest.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Xunit; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class ProjectDirectoryTest - { - [Fact] - public void ProjectDirectory_IsNotSetToBePreserved() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - - // Act & Assert - // This flag is only meant for local debugging and should not be set when checking in. - Assert.False(project.PreserveWorkingDirectory); - } - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PwaManifestTests.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PwaManifestTests.cs deleted file mode 100644 index 464d361e70..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PwaManifestTests.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.IO; -using System.Linq; -using System.Text.Json; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class PwaManifestTests - { - [Fact] - public async Task Build_ServiceWorkerAssetsManifest_Works() - { - // Arrange - var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" }; - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:ServiceWorkerAssetsManifest=service-worker-assets.js"); - - Assert.BuildPassed(result); - - var buildOutputDirectory = project.BuildOutputDirectory; - - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm"); - Assert.FileCountEquals(result, 1, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "wasm"), "dotnet.*.js"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. - - var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "standalone.StaticWebAssets.xml"); - Assert.FileContains(result, staticWebAssets, Path.Combine("netstandard2.1", "wwwroot")); - - var serviceWorkerAssetsManifest = Assert.FileExists(result, buildOutputDirectory, "wwwroot", "service-worker-assets.js"); - // Trim prefix 'self.assetsManifest = ' and suffix ';' - var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1]; - - var manifestContentsJson = JsonDocument.Parse(manifestContents); - Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets)); - Assert.Equal(JsonValueKind.Array, assets.ValueKind); - - var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray(); - Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e))); - } - - [Fact] - public async Task Publish_UpdatesServiceWorkerVersionHash_WhenSourcesChange() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); - - Assert.BuildPassed(result); - - var publishOutputDirectory = project.PublishOutputDirectory; - - var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js"); - var version = File.ReadAllLines(serviceWorkerFile).Last(); - var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/"); - Assert.True(match.Success); - Assert.Equal(2, match.Groups.Count); - Assert.NotNull(match.Groups[1].Value); - var capture = match.Groups[1].Value; - - // Act - var cssFile = Path.Combine(project.DirectoryPath, "LinkToWebRoot", "css", "app.css"); - File.WriteAllText(cssFile, ".updated { }"); - - // Assert - var updatedResult = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); - - Assert.BuildPassed(result); - - var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last(); - var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/"); - Assert.True(updatedMatch.Success); - Assert.Equal(2, updatedMatch.Groups.Count); - Assert.NotNull(updatedMatch.Groups[1].Value); - var updatedCapture = updatedMatch.Groups[1].Value; - - Assert.NotEqual(capture, updatedCapture); - } - - [Fact] - public async Task Publish_DeterministicAcrossBuilds_WhenNoSourcesChange() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); - - Assert.BuildPassed(result); - - var publishOutputDirectory = project.PublishOutputDirectory; - - var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js"); - var version = File.ReadAllLines(serviceWorkerFile).Last(); - var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/"); - Assert.True(match.Success); - Assert.Equal(2, match.Groups.Count); - Assert.NotNull(match.Groups[1].Value); - var capture = match.Groups[1].Value; - - // Act && Assert - var updatedResult = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); - - Assert.BuildPassed(result); - - var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last(); - var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/"); - Assert.True(updatedMatch.Success); - Assert.Equal(2, updatedMatch.Groups.Count); - Assert.NotNull(updatedMatch.Groups[1].Value); - var updatedCapture = updatedMatch.Groups[1].Value; - - Assert.Equal(capture, updatedCapture); - } - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/TestFacts.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/TestFacts.cs deleted file mode 100644 index 1da03c94c6..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/TestFacts.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq; -using System.Reflection; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public static class TestFacts - { - public static string DefaultNetCoreTargetFramework => - GetAttributeValue(nameof(DefaultNetCoreTargetFramework)); - - public static string RazorSdkDirectoryRoot => - GetAttributeValue(nameof(RazorSdkDirectoryRoot)); - - private static string GetAttributeValue(string name) - { - return Assembly - .GetExecutingAssembly() - .GetCustomAttributes() - .FirstOrDefault(a => a.Key == $"Testing.{name}") - .Value; - } - } -} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/WebAssemblyRuntimePackage.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/WebAssemblyRuntimePackage.cs deleted file mode 100644 index d95ba1e41c..0000000000 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/WebAssemblyRuntimePackage.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Reflection; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - internal static class WebAssemblyRuntimePackage - { - public static readonly string ComponentsWebAssemblyRuntimePackageVersion; - public static readonly string DotNetJsFileName; - - static WebAssemblyRuntimePackage() - { - ComponentsWebAssemblyRuntimePackageVersion = typeof(WebAssemblyRuntimePackage) - .Assembly - .GetCustomAttributes() - .FirstOrDefault(f => f.Key == "Testing.MicrosoftAspNetCoreComponentsWebAssemblyRuntimePackageVersion") - ?.Value - ?? throw new InvalidOperationException("Testing.MicrosoftAspNetCoreComponentsWebAssemblyRuntimePackageVersion was not found"); - - DotNetJsFileName = $"dotnet.{ComponentsWebAssemblyRuntimePackageVersion}.js"; - } - } -} diff --git a/src/Components/WebAssembly/Build/test/Microsoft.AspNetCore.Components.WebAssembly.Build.Tests.csproj b/src/Components/WebAssembly/Build/test/Microsoft.AspNetCore.Components.WebAssembly.Build.Tests.csproj deleted file mode 100644 index e7b4c82883..0000000000 --- a/src/Components/WebAssembly/Build/test/Microsoft.AspNetCore.Components.WebAssembly.Build.Tests.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - $(DefaultNetCoreTargetFramework) - - - $(DefaultItemExcludes);TestFiles\**\* - false - - - - - - - - - GENERATE_BASELINES;$(DefineConstants) - - - - TRACE - - - - - - - - - - - - - - - - - - - <_Parameter1>Testing.RepoRoot - <_Parameter2>$(RepoRoot) - - - - <_Parameter1>Testing.RazorSdkDirectoryRoot - <_Parameter2>$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\sdk-output\ - - - - <_Parameter1>Testing.MicrosoftAspNetCoreComponentsWebAssemblyRuntimePackageVersion - <_Parameter2>$(MicrosoftAspNetCoreComponentsWebAssemblyRuntimePackageVersion) - - - - <_Parameter1>Testing.DefaultNetCoreTargetFramework - <_Parameter2>$(DefaultNetCoreTargetFramework) - - - - - - <_TestAsset Include="..\testassets\standalone\standalone.csproj" /> - <_TestAsset Include="..\testassets\blazorhosted\blazorhosted.csproj" /> - - - - - - diff --git a/src/Components/WebAssembly/Build/test/RuntimeDependenciesResolverTest.cs b/src/Components/WebAssembly/Build/test/RuntimeDependenciesResolverTest.cs deleted file mode 100644 index 6fba8b34d9..0000000000 --- a/src/Components/WebAssembly/Build/test/RuntimeDependenciesResolverTest.cs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using Microsoft.AspNetCore.Testing; -using Xunit; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build -{ - public class RuntimeDependenciesResolverTest - { - [Fact] - [QuarantinedTest] - public void FindsReferenceAssemblyGraph_ForStandaloneApp() - { - // Arrange - var standaloneAppAssembly = typeof(StandaloneApp.Program).Assembly; - var mainAssemblyLocation = standaloneAppAssembly.Location; - - var hintPaths = ReadContent(standaloneAppAssembly, "StandaloneApp.referenceHints.txt"); - var bclLocations = ReadContent(standaloneAppAssembly, "StandaloneApp.bclLocations.txt"); - - var expectedContents = new[] - { - /* - The current Mono WASM BCL forwards from netstandard.dll to various facade assemblies - in which small bits of implementation live, such as System.Xml.XPath.XDocument. So - if you reference netstandard, then you also reference System.Xml.XPath.XDocument.dll, - even though you're very unlikely to be calling it at runtime. That's why the following - list (for a very basic Blazor app) is longer than you'd expect. - - These redundant references could be stripped out during publishing, but it's still - unfortunate that in development mode you'd see all these unexpected assemblies get - fetched from the server. We should try to get the Mono WASM BCL reorganized so that - all the implementation goes into mscorlib.dll, with the facade assemblies existing only - in case someone (or some 3rd party assembly) references them directly, but with their - implementations 100% forwarding to mscorlib.dll. Then in development you'd fetch far - fewer assemblies from the server, and during publishing, illink would remove all the - uncalled implementation code from mscorlib.dll anyway. - */ - "Microsoft.AspNetCore.Components.dll", - "Microsoft.AspNetCore.Components.Forms.dll", - "Microsoft.AspNetCore.Components.Web.dll", - "Microsoft.AspNetCore.Components.WebAssembly.dll", - "Microsoft.Bcl.AsyncInterfaces.dll", - "Microsoft.Extensions.Configuration.Abstractions.dll", - "Microsoft.Extensions.Configuration.dll", - "Microsoft.Extensions.Configuration.FileExtensions.dll", - "Microsoft.Extensions.Configuration.Json.dll", - "Microsoft.Extensions.DependencyInjection.Abstractions.dll", - "Microsoft.Extensions.DependencyInjection.dll", - "Microsoft.Extensions.FileProviders.Abstractions.dll", - "Microsoft.Extensions.FileProviders.Physical.dll", - "Microsoft.Extensions.FileSystemGlobbing.dll", - "Microsoft.Extensions.Logging.dll", - "Microsoft.Extensions.Logging.Abstractions.dll", - "Microsoft.Extensions.Options.dll", - "Microsoft.Extensions.Primitives.dll", - "Microsoft.JSInterop.dll", - "Microsoft.JSInterop.WebAssembly.dll", - "Mono.Security.dll", - "mscorlib.dll", - "netstandard.dll", - "StandaloneApp.dll", - "System.dll", - "System.Buffers.dll", - "System.Collections.Concurrent.dll", - "System.Collections.dll", - "System.ComponentModel.Annotations.dll", - "System.ComponentModel.DataAnnotations.dll", - "System.ComponentModel.Composition.dll", - "System.Core.dll", - "System.Data.dll", - "System.Data.DataSetExtensions.dll", - "System.Diagnostics.Debug.dll", - "System.Diagnostics.DiagnosticSource.dll", - "System.Diagnostics.Tracing.dll", - "System.Drawing.Common.dll", - "System.IO.Compression.dll", - "System.IO.Compression.FileSystem.dll", - "System.Memory.dll", - "System.Net.Http.dll", - "System.Net.Http.Json.dll", - "System.Net.Http.WebAssemblyHttpHandler.dll", - "System.Numerics.dll", - "System.Numerics.Vectors.dll", - "System.Reflection.dll", - "System.Resources.ResourceManager.dll", - "System.Runtime.Extensions.dll", - "System.Runtime.InteropServices.dll", - "System.Runtime.CompilerServices.Unsafe.dll", - "System.Runtime.Serialization.dll", - "System.Runtime.dll", - "System.ServiceModel.Internals.dll", - "System.Text.Encodings.Web.dll", - "System.Text.Json.dll", - "System.Threading.dll", - "System.Threading.Tasks.Extensions.dll", - "System.Transactions.dll", - "System.Xml.dll", - "System.Xml.Linq.dll", - "WebAssembly.Bindings.dll", - "WebAssembly.Net.WebSockets.dll", - }.OrderBy(i => i, StringComparer.Ordinal) - .ToArray(); - - // Act - - var paths = ResolveBlazorRuntimeDependencies - .ResolveRuntimeDependenciesCore( - mainAssemblyLocation, - hintPaths, - bclLocations); - - var contents = paths - .Select(p => Path.GetFileName(p)) - .Where(p => Path.GetExtension(p) != ".pdb") - .OrderBy(i => i, StringComparer.Ordinal) - .ToArray(); - - var expected = new HashSet(expectedContents); - var actual = new HashSet(contents); - - var contentNotFound = expected.Except(actual); - var additionalContentFound = actual.Except(expected); - - // Assert - if (contentNotFound.Any() || additionalContentFound.Any()) - { - throw new ContentMisMatchException - { - ContentNotFound = contentNotFound, - AdditionalContentFound = additionalContentFound, - }; - } - - Assert.Equal(expectedContents, contents); - } - - private string[] ReadContent(Assembly standaloneAppAssembly, string fileName) - { - using var resource = standaloneAppAssembly.GetManifestResourceStream(fileName); - using var streamReader = new StreamReader(resource); - - return streamReader.ReadToEnd().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); - } - - private class ContentMisMatchException : Xunit.Sdk.XunitException - { - public IEnumerable ContentNotFound { get; set; } - - public IEnumerable AdditionalContentFound { get; set; } - - public override string Message - { - get - { - var error = new StringBuilder(); - if (ContentNotFound.Any()) - { - error.Append($"Expected content not found: ") - .AppendJoin(", ", ContentNotFound); - } - - if (AdditionalContentFound.Any()) - { - error.Append("Unexpected content found: ") - .AppendJoin(", ", AdditionalContentFound); - } - - return error.ToString(); - } - } - } - } -} diff --git a/src/Components/WebAssembly/Build/testassets/Directory.Build.props b/src/Components/WebAssembly/Build/testassets/Directory.Build.props deleted file mode 100644 index 109d34f261..0000000000 --- a/src/Components/WebAssembly/Build/testassets/Directory.Build.props +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), global.json))\ - $(RepoRoot)src\Components\ - $(ComponentsRoot)WebAssembly\Build\src\ - $(BlazorBuildRoot)ReferenceBlazorBuildFromSource.props - - - netcoreapp3.1 - - false - false - - - - - - - - - - - diff --git a/src/Components/WebAssembly/Build/testassets/Directory.Build.targets b/src/Components/WebAssembly/Build/testassets/Directory.Build.targets deleted file mode 100644 index 8c119d5413..0000000000 --- a/src/Components/WebAssembly/Build/testassets/Directory.Build.targets +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/Components/WebAssembly/Build/testassets/blazorhosted/blazorhosted.csproj b/src/Components/WebAssembly/Build/testassets/blazorhosted/blazorhosted.csproj deleted file mode 100644 index bcdc9dba7d..0000000000 --- a/src/Components/WebAssembly/Build/testassets/blazorhosted/blazorhosted.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - net5.0 - - - - - - - diff --git a/src/Components/WebAssembly/Build/testassets/standalone/standalone.csproj b/src/Components/WebAssembly/Build/testassets/standalone/standalone.csproj deleted file mode 100644 index bbeafa77c2..0000000000 --- a/src/Components/WebAssembly/Build/testassets/standalone/standalone.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - - netstandard2.1 - 3.0 - custom-service-worker-assets.js - - - - - - - - - - - - - - - - - wwwroot\ - - - - - - - - - - - - - - - - diff --git a/src/Components/WebAssembly/Compression/src/Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression.csproj b/src/Components/WebAssembly/Compression/src/Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression.csproj deleted file mode 100644 index 7267b5ed54..0000000000 --- a/src/Components/WebAssembly/Compression/src/Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - $(DefaultNetCoreTargetFramework) - Exe - blazor-brotli - false - - - diff --git a/src/Components/WebAssembly/Compression/src/Program.cs b/src/Components/WebAssembly/Compression/src/Program.cs deleted file mode 100644 index 804129166b..0000000000 --- a/src/Components/WebAssembly/Compression/src/Program.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.IO; -using System.IO.Compression; -using System.Text.Json; -using System.Threading.Tasks; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression -{ - class Program - { - private const int _error = -1; - - static async Task Main(string[] args) - { - if (args.Length != 1) - { - Console.Error.WriteLine("Invalid argument count. Usage: 'blazor-brotli <>'"); - return _error; - } - - var manifestPath = args[0]; - if (!File.Exists(manifestPath)) - { - Console.Error.WriteLine($"Manifest '{manifestPath}' does not exist."); - return -1; - } - - using var manifestStream = File.OpenRead(manifestPath); - - var manifest = await JsonSerializer.DeserializeAsync(manifestStream); - var result = 0; - Parallel.ForEach(manifest.FilesToCompress, (file) => - { - var inputPath = file.Source; - var inputSource = file.InputSource; - var targetCompressionPath = file.Target; - - if (!File.Exists(inputSource)) - { - Console.WriteLine($"Skipping '{inputPath}' because '{inputSource}' does not exist."); - return; - } - - if (File.Exists(targetCompressionPath) && File.GetLastWriteTimeUtc(inputSource) < File.GetLastWriteTimeUtc(targetCompressionPath)) - { - // Incrementalism. If input source doesn't exist or it exists and is not newer than the expected output, do nothing. - Console.WriteLine($"Skipping '{inputPath}' because '{targetCompressionPath}' is newer than '{inputSource}'."); - return; - } - - try - { - Directory.CreateDirectory(Path.GetDirectoryName(targetCompressionPath)); - - using var sourceStream = File.OpenRead(inputPath); - using var fileStream = new FileStream(targetCompressionPath, FileMode.Create); - - var compressionLevel = CompressionLevel.Optimal; - if (Environment.GetEnvironmentVariable("_BlazorWebAssemblyBuildTest_BrotliCompressionLevel_NoCompression") == "1") - { - compressionLevel = CompressionLevel.NoCompression; - } - using var stream = new BrotliStream(fileStream, compressionLevel); - - sourceStream.CopyTo(stream); - } - catch (Exception e) - { - Console.Error.WriteLine(e); - result = -1; - } - }); - - return result; - } - - private class ManifestData - { - public CompressedFile[] FilesToCompress { get; set; } - } - - private class CompressedFile - { - public string Source { get; set; } - - public string InputSource { get; set; } - - public string Target { get; set; } - } - } -} diff --git a/src/Components/WebAssembly/Compression/src/runtimeconfig.template.json b/src/Components/WebAssembly/Compression/src/runtimeconfig.template.json deleted file mode 100644 index f022b7ffce..0000000000 --- a/src/Components/WebAssembly/Compression/src/runtimeconfig.template.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rollForwardOnNoCandidateFx": 2 -} \ No newline at end of file diff --git a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index 0447c61b7e..2aab0dbca7 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -15,6 +15,14 @@ + + + + + ..\..\..\Web.JS\dist\$(Configuration)\blazor.webassembly.js + + + + + + diff --git a/src/Components/WebAssembly/WebAssembly/src/build/netstandard2.0/Microsoft.AspNetCore.Components.WebAssembly.props b/src/Components/WebAssembly/WebAssembly/src/build/netstandard2.0/Microsoft.AspNetCore.Components.WebAssembly.props new file mode 100644 index 0000000000..41ae67bf35 --- /dev/null +++ b/src/Components/WebAssembly/WebAssembly/src/build/netstandard2.0/Microsoft.AspNetCore.Components.WebAssembly.props @@ -0,0 +1,5 @@ + + + $(MSBuildThisFileDirectory)blazor.webassembly.js + + diff --git a/src/Components/WebAssembly/testassets/HostedInAspNet.Client/HostedInAspNet.Client.csproj b/src/Components/WebAssembly/testassets/HostedInAspNet.Client/HostedInAspNet.Client.csproj index 84dae61bf7..4994c3eb7d 100644 --- a/src/Components/WebAssembly/testassets/HostedInAspNet.Client/HostedInAspNet.Client.csproj +++ b/src/Components/WebAssembly/testassets/HostedInAspNet.Client/HostedInAspNet.Client.csproj @@ -1,12 +1,10 @@ - netstandard2.1 - Exe - true - 3.0 - - false + $(DefaultNetCoreTargetFramework) + browser-wasm + true + true diff --git a/src/Components/WebAssembly/testassets/MonoSanity/MonoSanity.csproj b/src/Components/WebAssembly/testassets/MonoSanity/MonoSanity.csproj deleted file mode 100644 index 00b73a99ed..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanity/MonoSanity.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - $(DefaultNetCoreTargetFramework) - - - - - - - - - - - - diff --git a/src/Components/WebAssembly/testassets/MonoSanity/Program.cs b/src/Components/WebAssembly/testassets/MonoSanity/Program.cs deleted file mode 100644 index 6481e4d4bc..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanity/Program.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; - -namespace MonoSanity -{ - public class Program - { - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IHost BuildWebHost(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webHostBuilder => - { - // We require this line because we run in Production environment - // and static web assets are only on by default during development. - webHostBuilder.UseStaticWebAssets(); - webHostBuilder.UseStartup(); - }) - .Build(); - } -} diff --git a/src/Components/WebAssembly/testassets/MonoSanity/Startup.cs b/src/Components/WebAssembly/testassets/MonoSanity/Startup.cs deleted file mode 100644 index 3cf349e787..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanity/Startup.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -namespace MonoSanity -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - } - - public void Configure(IApplicationBuilder app) - { - app.UseDeveloperExceptionPage(); - app.UseFileServer(new FileServerOptions() { EnableDefaultFiles = true, }); - app.UseBlazorFrameworkFiles(); - app.UseStaticFiles(); - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapFallbackToFile("index.html"); - }); - } - } -} diff --git a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/index.html b/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/index.html deleted file mode 100644 index 8a42e8e5d1..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/index.html +++ /dev/null @@ -1,158 +0,0 @@ - - - - Mono sanity check - - - -

Simple sanity check to ensure the Mono runtime works in basic cases.

- -
- Add numbers -
- + - = - - -
-
- -
- Repeat string -
- * - = - - -
-
- -
- Trigger .NET exception -
- - -
-
-
- -
- Call JS from .NET -
- - -
-
-
- -
- Call JS from .NET (no boxing) -
- / - = - - -
-
- -
- Get runtime OS -
- - -
-
- -

Loading...

- - - - - diff --git a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js b/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js deleted file mode 100644 index ad004cee37..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js +++ /dev/null @@ -1,128 +0,0 @@ -(function () { - // Implement just enough of the DotNet.* API surface for unmarshalled interop calls to work - // in the cases used in this project - window.DotNet = { - jsCallDispatcher: { - findJSFunction: function (identifier) { - return window[identifier]; - } - } - }; - - window.initMono = function initMono(loadAssemblyUrls, onReadyCallback) { - window.Module = { - locateFile: function (fileName) { - return fileName === 'dotnet.wasm' ? '/_framework/wasm/dotnet.wasm' : fileName; - }, - onRuntimeInitialized: function () { - var allAssemblyUrls = loadAssemblyUrls.concat([ - 'netstandard.dll', - 'mscorlib.dll', - 'System.dll', - 'System.Core.dll', - 'System.Net.Http.dll', - 'System.Net.Http.WebAssemblyHttpHandler.dll', - 'WebAssembly.Bindings.dll' - ]); - - // For these tests we're using Mono's built-in mono_load_runtime_and_bcl util. - // In real apps we don't use this because we want to have more fine-grained - // control over how the requests are issued, what gets logged, etc., so for - // real apps Blazor's Boot.WebAssembly.ts implements its own equivalent. - MONO.mono_load_runtime_and_bcl( - /* vfx_prefix */ 'myapp', // Virtual filesystem root - arbitrary value - /* deploy_prefix */ '_framework/_bin', - /* enable_debugging */ 1, - allAssemblyUrls, - onReadyCallback - ); - } - }; - - addScriptTagsToDocument(); - }; - - window.invokeMonoMethod = function invokeMonoMethod(assemblyName, namespace, typeName, methodName, args) { - var assembly_load = Module.cwrap('mono_wasm_assembly_load', 'number', ['string']); - var find_class = Module.cwrap('mono_wasm_assembly_find_class', 'number', ['number', 'string', 'string']); - var find_method = Module.cwrap('mono_wasm_assembly_find_method', 'number', ['number', 'string', 'number']); - - var assembly = assembly_load(assemblyName); - var type = find_class(assembly, namespace, typeName); - var method = find_method(type, methodName, -1); - - var stack = Module.stackSave(); - try { - var resultPtr = callMethod(method, null, args); - return dotnetStringToJavaScriptString(resultPtr); - } - finally { - Module.stackRestore(stack); - } - }; - - window.dotnetStringToJavaScriptString = function dotnetStringToJavaScriptString(mono_obj) { - if (mono_obj === 0) - return null; - var mono_string_get_utf8 = Module.cwrap('mono_wasm_string_get_utf8', 'number', ['number']); - var raw = mono_string_get_utf8(mono_obj); - var res = Module.UTF8ToString(raw); - Module._free(raw); - return res; - }; - - window.javaScriptStringToDotNetString = function dotnetStringToJavaScriptString(javaScriptString) { - var mono_string = Module.cwrap('mono_wasm_string_from_js', 'number', ['string']); - return mono_string(javaScriptString); - }; - - function callMethod(method, target, args) { - var stack = Module.stackSave(); - var invoke_method = Module.cwrap('mono_wasm_invoke_method', 'number', ['number', 'number', 'number']); - - try { - var argsBuffer = Module.stackAlloc(args.length); - var exceptionFlagManagedInt = Module.stackAlloc(4); - for (var i = 0; i < args.length; ++i) { - var argVal = args[i]; - if (typeof argVal === 'number') { - var managedInt = Module.stackAlloc(4); - Module.setValue(managedInt, argVal, 'i32'); - Module.setValue(argsBuffer + i * 4, managedInt, 'i32'); - } else if (typeof argVal === 'string') { - var managedString = javaScriptStringToDotNetString(argVal); - Module.setValue(argsBuffer + i * 4, managedString, 'i32'); - } else { - throw new Error('Unsupported arg type: ' + typeof argVal); - } - } - Module.setValue(exceptionFlagManagedInt, 0, 'i32'); - - var res = invoke_method(method, target, argsBuffer, exceptionFlagManagedInt); - - if (Module.getValue(exceptionFlagManagedInt, 'i32') !== 0) { - throw new Error(dotnetStringToJavaScriptString(res)); - } - - return res; - } finally { - Module.stackRestore(stack); - } - } - - async function addScriptTagsToDocument() { - var browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate; - if (!browserSupportsNativeWebAssembly) { - throw new Error('This browser does not support WebAssembly.'); - } - - var bootJson = await fetch('/_framework/blazor.boot.json').then(res => res.json()); - var dotNetJsResourceName = Object.keys(bootJson.resources.runtime) - .filter(name => name.endsWith('.js')); - - var scriptElem = document.createElement('script'); - scriptElem.src = '/_framework/wasm/' + dotNetJsResourceName; - document.body.appendChild(scriptElem); - } - -})(); diff --git a/src/Components/WebAssembly/testassets/MonoSanityClient/Examples.cs b/src/Components/WebAssembly/testassets/MonoSanityClient/Examples.cs deleted file mode 100644 index 7bae673449..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanityClient/Examples.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using WebAssembly.JSInterop; -using System; -using System.Runtime.InteropServices; -using System.Text; -using System.Net.Http; - -namespace MonoSanityClient -{ - public static class Examples - { - public static string AddNumbers(int a, int b) - => (a + b).ToString(); - - public static string RepeatString(string str, int count) - { - var result = new StringBuilder(); - - for (var i = 0; i < count; i++) - { - result.Append(str); - } - - return result.ToString(); - } - - public static void TriggerException(string message) - { - throw new InvalidOperationException(message); - } - - public static string EvaluateJavaScript(string expression) - { - var result = InternalCalls.InvokeJSUnmarshalled(out var exceptionMessage, "evaluateJsExpression", expression, null, null); - if (exceptionMessage != null) - { - return $".NET got exception: {exceptionMessage}"; - } - - return $".NET received: {(result ?? "(NULL)")}"; - } - - public static string CallJsNoBoxing(int numberA, int numberB) - { - // For tests that call this method, we'll exercise the 'BlazorInvokeJS' code path - // since that doesn't box the params - var result = InternalCalls.InvokeJSUnmarshalled(out var exceptionMessage, "divideNumbersUnmarshalled", numberA, numberB, null); - if (exceptionMessage != null) - { - return $".NET got exception: {exceptionMessage}"; - } - - return $".NET received: {result}"; - } - - public static string GetRuntimeInformation() - => $"OSDescription: '{RuntimeInformation.OSDescription}';" - + $" OSArchitecture: '{RuntimeInformation.OSArchitecture}';" - + $" IsOSPlatform(BROWSER): '{RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))}'"; - } -} diff --git a/src/Components/WebAssembly/testassets/MonoSanityClient/InternalCalls.cs b/src/Components/WebAssembly/testassets/MonoSanityClient/InternalCalls.cs deleted file mode 100644 index 0fb15337e8..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanityClient/InternalCalls.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.CompilerServices; - -namespace WebAssembly.JSInterop -{ - // This file is copied from https://github.com/dotnet/jsinterop/blob/master/src/Mono.WebAssembly.Interop/InternalCalls.cs - // so that MonoSanityClient can directly use the same underlying interop APIs (because - // we're trying to observe the behavior of the Mono runtime itself, not JSInterop). - - internal class InternalCalls - { - // The exact namespace, type, and method names must match the corresponding entries - // in driver.c in the Mono distribution - - // We're passing asyncHandle by ref not because we want it to be writable, but so it gets - // passed as a pointer (4 bytes). We can pass 4-byte values, but not 8-byte ones. - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern string InvokeJSMarshalled(out string exception, ref long asyncHandle, string functionIdentifier, string argsJson); - - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern TRes InvokeJSUnmarshalled(out string exception, string functionIdentifier, T0 arg0, T1 arg1, T2 arg2); - } -} diff --git a/src/Components/WebAssembly/testassets/MonoSanityClient/MonoSanityClient.csproj b/src/Components/WebAssembly/testassets/MonoSanityClient/MonoSanityClient.csproj deleted file mode 100644 index 9df38c64eb..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanityClient/MonoSanityClient.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - netstandard2.1 - false - exe - 3.0 - - true - - true - - diff --git a/src/Components/WebAssembly/testassets/MonoSanityClient/Program.cs b/src/Components/WebAssembly/testassets/MonoSanityClient/Program.cs deleted file mode 100644 index 3bba6ffba6..0000000000 --- a/src/Components/WebAssembly/testassets/MonoSanityClient/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace MonoSanityClient -{ - // Note: Not used at runtime. This exists only to give the server app some type to reference. - // In realistic scenarios you'd have a Program class for real. - - public class Program - { - static void Main() - { - } - } -} diff --git a/src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj b/src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj index 8959978daa..9ebf31c365 100644 --- a/src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj +++ b/src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj @@ -1,9 +1,9 @@ - netstandard2.1 - true - 3.0 + net5.0 + browser-wasm + true true diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Wasm.Authentication.Client.csproj b/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Wasm.Authentication.Client.csproj index 6ade900b99..16fddd3ddc 100644 --- a/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Wasm.Authentication.Client.csproj +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Wasm.Authentication.Client.csproj @@ -1,9 +1,10 @@ - netstandard2.1 - 3.0 - true + $(DefaultNetCoreTargetFramework) + browser-wasm + true + true diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj index 57de501f63..54ec8b638d 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj @@ -1,9 +1,9 @@ - netstandard2.1 - 3.0 - true + net5.0 + browser-wasm + true true diff --git a/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj b/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj index fb063e00c8..256ea831e1 100644 --- a/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj +++ b/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj @@ -40,8 +40,6 @@ - - diff --git a/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs b/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs index f273f5e53f..1dd32e8221 100644 --- a/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs +++ b/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs @@ -48,7 +48,6 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests var initialResourcesRequested = GetAndClearRequestedPaths(); Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/blazor.boot.json"))); Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/dotnet.wasm"))); - Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/dotnet.timezones.dat"))); Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith(".js"))); Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith(".dll"))); @@ -60,7 +59,6 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests var subsequentResourcesRequested = GetAndClearRequestedPaths(); Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/blazor.boot.json"))); Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith("/dotnet.wasm"))); - Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith("/dotnet.timezones.dat"))); Assert.NotEmpty(subsequentResourcesRequested.Where(path => path.EndsWith(".js"))); Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith(".dll"))); } diff --git a/src/Components/test/E2ETest/Tests/MonoSanityTest.cs b/src/Components/test/E2ETest/Tests/MonoSanityTest.cs deleted file mode 100644 index 181a1f9974..0000000000 --- a/src/Components/test/E2ETest/Tests/MonoSanityTest.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; -using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; -using Microsoft.AspNetCore.E2ETesting; -using OpenQA.Selenium; -using OpenQA.Selenium.Support.UI; -using System; -using System.Threading.Tasks; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.AspNetCore.Components.E2ETest.Tests -{ - public class MonoSanityTest : ServerTestBase - { - public MonoSanityTest( - BrowserFixture browserFixture, - AspNetSiteServerFixture serverFixture, - ITestOutputHelper output) - : base(browserFixture, serverFixture, output) - { - serverFixture.BuildWebHostMethod = MonoSanity.Program.BuildWebHost; - } - - protected override void InitializeAsyncCore() - { - Navigate("/", noReload: true); - WaitUntilMonoRunningInBrowser(); - } - - private void WaitUntilMonoRunningInBrowser() - { - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(driver => - { - return ((IJavaScriptExecutor)driver) - .ExecuteScript("return window.isTestReady;"); - }); - } - - [Fact] - public void HasTitle() - { - Assert.Equal("Mono sanity check", Browser.Title); - } - - [Fact] - public void CanAddNumbers() - { - SetValue(Browser, "addNumberA", "1001"); - SetValue(Browser, "addNumberB", "2002"); - Browser.FindElement(By.CssSelector("#addNumbers button")).Click(); - - Assert.Equal("3003", GetValue(Browser, "addNumbersResult")); - } - - [Fact] - public void CanRepeatString() - { - SetValue(Browser, "repeatStringStr", "Test"); - SetValue(Browser, "repeatStringCount", "5"); - Browser.FindElement(By.CssSelector("#repeatString button")).Click(); - - Assert.Equal("TestTestTestTestTest", GetValue(Browser, "repeatStringResult")); - } - - [Fact] - public void CanReceiveDotNetExceptionInJavaScript() - { - SetValue(Browser, "triggerExceptionMessage", "Hello from test"); - Browser.FindElement(By.CssSelector("#triggerException button")).Click(); - - Assert.Contains("Hello from test", GetValue(Browser, "triggerExceptionMessageStackTrace")); - } - - [Fact] - public void CanCallJavaScriptFromDotNet() - { - SetValue(Browser, "callJsEvalExpression", "getUserAgentString()"); - Browser.FindElement(By.CssSelector("#callJs button")).Click(); - var result = GetValue(Browser, "callJsResult"); - Assert.StartsWith(".NET received: Mozilla", result); - } - - [Fact] - public void CanReceiveJavaScriptExceptionInDotNet() - { - SetValue(Browser, "callJsEvalExpression", "triggerJsException()"); - Browser.FindElement(By.CssSelector("#callJs button")).Click(); - var result = GetValue(Browser, "callJsResult"); - Assert.StartsWith(".NET got exception: This is a JavaScript exception.", result); - - // Also verify we got a stack trace - Assert.Contains("at triggerJsException", result); - } - - [Fact] - public void CanEvaluateJsExpressionThatResultsInNull() - { - SetValue(Browser, "callJsEvalExpression", "null"); - Browser.FindElement(By.CssSelector("#callJs button")).Click(); - var result = GetValue(Browser, "callJsResult"); - Assert.Equal(".NET received: (NULL)", result); - } - - [Fact] - public void CanEvaluateJsExpressionThatResultsInUndefined() - { - SetValue(Browser, "callJsEvalExpression", "console.log('Not returning anything')"); - Browser.FindElement(By.CssSelector("#callJs button")).Click(); - var result = GetValue(Browser, "callJsResult"); - Assert.Equal(".NET received: (NULL)", result); - } - - [Fact] - public void CanCallJsFunctionsWithoutBoxing() - { - SetValue(Browser, "callJsNoBoxingNumberA", "108"); - SetValue(Browser, "callJsNoBoxingNumberB", "4"); - Browser.FindElement(By.CssSelector("#callJsNoBoxing button")).Click(); - Assert.Equal(".NET received: 27", GetValue(Browser, "callJsNoBoxingResult")); - } - - [Fact] - public void CanCallJsFunctionsWithoutBoxingAndReceiveException() - { - SetValue(Browser, "callJsNoBoxingNumberA", "1"); - SetValue(Browser, "callJsNoBoxingNumberB", "0"); - Browser.FindElement(By.CssSelector("#callJsNoBoxing button")).Click(); - - Assert.StartsWith(".NET got exception: Division by zero", GetValue(Browser, "callJsNoBoxingResult")); - } - - [Fact] - public void ReturnsExpectedRuntimeInformation() - { - Browser.FindElement(By.CssSelector("#getRuntimeInformation button")).Click(); - Assert.Equal( - "OSDescription: 'web'; OSArchitecture: 'X86'; IsOSPlatform(BROWSER): 'True'", - GetValue(Browser, "getRuntimeInformationResult")); - } - - private static string GetValue(IWebDriver webDriver, string elementId) - { - var element = webDriver.FindElement(By.Id(elementId)); - return element.GetAttribute("value"); - } - - private static void SetValue(IWebDriver webDriver, string elementId, string value) - { - var element = webDriver.FindElement(By.Id(elementId)); - element.Clear(); - element.SendKeys(value); - } - } -} diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs b/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs index 8fce8f9513..998144b33c 100644 --- a/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs +++ b/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs @@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests [Fact] public void WasmAuthentication_Loads() { - Assert.Equal("Wasm.Authentication.Client", Browser.Title); + Browser.Equal("Wasm.Authentication.Client", () => Browser.Title); } [Fact] @@ -408,8 +408,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests private void WaitUntilLoaded(bool skipHeader = false) { - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( - driver => driver.FindElement(By.TagName("app")).Text != "Loading..."); + Browser.Exists(By.TagName("app")); + Browser.True(() => Browser.FindElement(By.TagName("app")).Text != "Loading..."); if (!skipHeader) { diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyGlobalizationTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyGlobalizationTest.cs index bf5fcb6f62..a18343dd75 100644 --- a/src/Components/test/E2ETest/Tests/WebAssemblyGlobalizationTest.cs +++ b/src/Components/test/E2ETest/Tests/WebAssemblyGlobalizationTest.cs @@ -10,11 +10,13 @@ using OpenQA.Selenium; using Xunit; using Xunit.Abstractions; -namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests +namespace Microsoft.AspNetCore.Components.E2ETest.Tests { // For now this is limited to server-side execution because we don't have the ability to set the // culture in client-side Blazor. - public class WebAssemblyGlobalizationTest : GlobalizationTest> + // This type is internal since localization currently does not work. + // Make it public onc https://github.com/dotnet/runtime/issues/38124 is resolved. + internal class WebAssemblyGlobalizationTest : GlobalizationTest> { public WebAssemblyGlobalizationTest( BrowserFixture browserFixture, diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs index 81c99ccf67..184c62e33c 100644 --- a/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs +++ b/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests { } - [Theory] + [Theory(Skip = "https://github.com/dotnet/runtime/issues/38124")] [InlineData("en-US", "Hello!")] [InlineData("fr-FR", "Bonjour!")] public void CanSetCultureAndReadLocalizedResources(string culture, string message) diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyLoggingTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyLoggingTest.cs index 5d83568f50..c6440eecd8 100644 --- a/src/Components/test/E2ETest/Tests/WebAssemblyLoggingTest.cs +++ b/src/Components/test/E2ETest/Tests/WebAssemblyLoggingTest.cs @@ -52,7 +52,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests AssertLogContainsCriticalMessages( "crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]", "[Custom logger] Unhandled exception rendering component: Here is the outer exception", - "System.InvalidTimeZoneException: Here is the outer exception ---> System.ArithmeticException: Here is the inner exception", + "System.InvalidTimeZoneException: Here is the outer exception", + "System.ArithmeticException: Here is the inner exception", "at BasicTestApp.ErrorComponent.ThrowInner"); } diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs index 9a38268403..61eb71c618 100644 --- a/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs +++ b/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs @@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests { } - [Fact] + [Fact(Skip = "https://github.com/dotnet/runtime/issues/38126")] public void InvariantCultureWorksAsExpected() { Navigate(ServerPathBase, noReload: false); diff --git a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj index 1cba39fcde..3923c0d5c0 100644 --- a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj +++ b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj @@ -1,10 +1,10 @@ - netstandard2.1 - 3.0 + $(DefaultNetCoreTargetFramework) + true + browser-wasm - true --pathbase /subdir diff --git a/src/Components/test/testassets/BasicTestApp/Program.cs b/src/Components/test/testassets/BasicTestApp/Program.cs index 01d7c29f00..b512810bc1 100644 --- a/src/Components/test/testassets/BasicTestApp/Program.cs +++ b/src/Components/test/testassets/BasicTestApp/Program.cs @@ -42,7 +42,7 @@ namespace BasicTestApp builder.Logging.Services.AddSingleton(s => new PrependMessageLoggerProvider(builder.Configuration["Logging:PrependMessage:Message"], s.GetService())); - + var host = builder.Build(); ConfigureCulture(host); diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js b/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js index a1f7975a90..dbbba1e0c8 100644 --- a/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js +++ b/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js @@ -258,4 +258,4 @@ function receiveDotNetObjectByRefAsync(incomingData) { testDto: testDto }; }); -} +} \ No newline at end of file diff --git a/src/Components/test/testassets/ComponentsApp.App/ComponentsApp.App.csproj b/src/Components/test/testassets/ComponentsApp.App/ComponentsApp.App.csproj index 470c119f13..2680ed8709 100644 --- a/src/Components/test/testassets/ComponentsApp.App/ComponentsApp.App.csproj +++ b/src/Components/test/testassets/ComponentsApp.App/ComponentsApp.App.csproj @@ -1,8 +1,7 @@  - netstandard2.0 - 3.0 + $(DefaultNetCoreTargetFramework) diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj index f5ae6e70b0..d9d6b2dda8 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj @@ -71,10 +71,6 @@ <_Parameter2>$(TargetFramework) <_Parameter3> - - <_Parameter1>Test.RazorSdkDirectoryRoot - <_Parameter2>$(RazorSdkDirectoryRoot) - diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs index 9050f00b15..ffa5da4de1 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs @@ -44,7 +44,7 @@ namespace Templates.Test public async Task BlazorWasmStandaloneTemplate_Works() { var project = await ProjectFactory.GetOrCreateProject("blazorstandalone", Output); - project.TargetFramework = "netstandard2.1"; + project.RuntimeIdentifier = "browser-wasm"; var createResult = await project.RunDotNetNewAsync("blazorwasm"); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); @@ -135,7 +135,7 @@ namespace Templates.Test public async Task BlazorWasmStandalonePwaTemplate_Works() { var project = await ProjectFactory.GetOrCreateProject("blazorstandalonepwa", Output); - project.TargetFramework = "netstandard2.1"; + project.RuntimeIdentifier = "browser-wasm"; var createResult = await project.RunDotNetNewAsync("blazorwasm", args: new[] { "--pwa" }); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); @@ -457,8 +457,6 @@ namespace Templates.Test ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process)); await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); - // We only do brotli precompression for published apps - await AssertCompressionFormat(aspNetProcess, "gzip"); if (BrowserFixture.IsHostAutomationSupported()) { aspNetProcess.VisitInBrowser(Browser); @@ -597,7 +595,6 @@ namespace Templates.Test File.WriteAllText(Path.Combine(serverProject.TemplatePublishDir, "appsettings.json"), testAppSettings); } - private (ProcessEx, string url) RunPublishedStandaloneBlazorProject(Project project) { var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot"); diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/TemplateTests.props.in b/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/TemplateTests.props.in index 0b1eeedf1b..1ffcbf83d1 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/TemplateTests.props.in +++ b/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/TemplateTests.props.in @@ -34,5 +34,8 @@ several versions older than latest. To avoid a cyclical dependency, this package reference is added to override the bundled version. Since this is a project reference, we must explicitly import the props file and also specify the output location of the SDK directory. --> + + ${ArtifactsBinDir}Microsoft.NET.Sdk.Razor\${Configuration}\sdk-output\ + diff --git a/src/ProjectTemplates/Shared/Project.cs b/src/ProjectTemplates/Shared/Project.cs index 505716e01e..939a49089f 100644 --- a/src/ProjectTemplates/Shared/Project.cs +++ b/src/ProjectTemplates/Shared/Project.cs @@ -39,10 +39,10 @@ namespace Templates.Test.Helpers public string ProjectGuid { get; set; } public string TemplateOutputDir { get; set; } public string TargetFramework { get; set; } = GetAssemblyMetadata("Test.DefaultTargetFramework"); - public string RazorSdkDirectoryRoot { get; set; } = GetAssemblyMetadata("Test.RazorSdkDirectoryRoot"); + public string RuntimeIdentifier { get; set; } = string.Empty; - public string TemplateBuildDir => Path.Combine(TemplateOutputDir, "bin", "Debug", TargetFramework); - public string TemplatePublishDir => Path.Combine(TemplateOutputDir, "bin", "Release", TargetFramework, "publish"); + public string TemplateBuildDir => Path.Combine(TemplateOutputDir, "bin", "Debug", TargetFramework, RuntimeIdentifier); + public string TemplatePublishDir => Path.Combine(TemplateOutputDir, "bin", "Release", TargetFramework, RuntimeIdentifier, "publish"); public ITestOutputHelper Output { get; set; } public IMessageSink DiagnosticsMessageSink { get; set; } @@ -117,9 +117,7 @@ namespace Templates.Test.Helpers // Avoid restoring as part of build or publish. These projects should have already restored as part of running dotnet new. Explicitly disabling restore // should avoid any global contention and we can execute a build or publish in a lock-free way - var razorSDKarg = string.IsNullOrEmpty(RazorSdkDirectoryRoot) ? string.Empty : $"/p:RazorSdkDirectoryRoot={RazorSdkDirectoryRoot}"; - - using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"publish --no-restore -c Release /bl {razorSDKarg} {additionalArgs}", packageOptions); + using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"publish --no-restore -c Release /bl {additionalArgs}", packageOptions); await result.Exited; CaptureBinLogOnFailure(result); return new ProcessResult(result); @@ -132,9 +130,7 @@ namespace Templates.Test.Helpers // Avoid restoring as part of build or publish. These projects should have already restored as part of running dotnet new. Explicitly disabling restore // should avoid any global contention and we can execute a build or publish in a lock-free way - var razorSDKarg = string.IsNullOrEmpty(RazorSdkDirectoryRoot) ? string.Empty : $"/p:RazorSdkDirectoryRoot={RazorSdkDirectoryRoot}"; - - using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"build --no-restore -c Debug /bl {razorSDKarg} {additionalArgs}", packageOptions); + using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"build --no-restore -c Debug /bl {additionalArgs}", packageOptions); await result.Exited; CaptureBinLogOnFailure(result); return new ProcessResult(result); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Client.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Client.csproj.in index 4b856535b2..24c38f6480 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Client.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Client.csproj.in @@ -1,8 +1,9 @@ - netstandard2.1 - 3.0 + ${DefaultNetCoreTargetFramework} + browser-wasm + true service-worker-assets.js @@ -13,7 +14,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Shared.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Shared.csproj.in index d4c395e8cb..797f21c216 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Shared.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Shared.csproj.in @@ -1,7 +1,7 @@ - netstandard2.1 + ${DefaultNetCoreTargetFramework} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj index d36a8d5050..e142e3233a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj +++ b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj @@ -35,7 +35,6 @@ - diff --git a/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in b/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in index 227bffd7a5..c3e1a9634d 100644 --- a/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in +++ b/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in @@ -34,5 +34,8 @@ We reference the project to ensure it's built before the other projects that use it. Since this is a project reference, we must explicitly import the props file and also specify the output location of the SDK directory. --> + + ${ArtifactsBinDir}Microsoft.NET.Sdk.Razor\${Configuration}\sdk-output\ + diff --git a/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj b/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj index 5bc7af6a32..69e08a8c83 100644 --- a/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj +++ b/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -65,10 +65,6 @@ <_Parameter1>Test.DefaultTargetFramework <_Parameter2>$(DefaultNetCoreTargetFramework) - - <_Parameter1>Test.RazorSdkDirectoryRoot - <_Parameter2>$(RazorSdkDirectoryRoot) - <_Parameter1>ContinuousIntegrationBuild <_Parameter2>true diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/Application.cs b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/Application.cs index 9c63203f66..73a05d570f 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/Application.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/Application.cs @@ -39,6 +39,7 @@ namespace Microsoft.AspNetCore.Razor.Tools Commands.Add(new ShutdownCommand(this)); Commands.Add(new DiscoverCommand(this)); Commands.Add(new GenerateCommand(this)); + Commands.Add(new BrotliCompressCommand(this)); } public CancellationToken CancellationToken { get; } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/BrotliCompressCommand.cs b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/BrotliCompressCommand.cs new file mode 100644 index 0000000000..ca758cd6d3 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/BrotliCompressCommand.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.Threading.Tasks; +using Microsoft.Extensions.CommandLineUtils; + +namespace Microsoft.AspNetCore.Razor.Tools +{ + internal class BrotliCompressCommand : CommandBase + { + public BrotliCompressCommand(Application parent) + : base(parent, "brotli") + { + Sources = Option("-s", "files to compress", CommandOptionType.MultipleValue); + Outputs = Option("-o", "Output file path", CommandOptionType.MultipleValue); + CompressionLevelOption = Option("-c", "Compression level", CommandOptionType.SingleValue); + } + + public CommandOption Sources { get; } + + public CommandOption Outputs { get; } + + public CommandOption CompressionLevelOption { get; } + + public CompressionLevel CompressionLevel { get; private set; } = CompressionLevel.Optimal; + + protected override 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; + } + + protected override 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(ExitCodeSuccess); + } + } +} diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs index 72e8d79e49..8648ede045 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs @@ -9,6 +9,7 @@ using System.IO.Compression; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; @@ -238,6 +239,26 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } } + public static void FileHashEquals(MSBuildResult result, string filePath, string expectedSha256Base64) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + filePath = Path.Combine(result.Project.DirectoryPath, filePath); + FileExists(result, filePath); + + var actual = File.ReadAllBytes(filePath); + using var algorithm = SHA256.Create(); + var actualSha256 = algorithm.ComputeHash(actual); + var actualSha256Base64 = Convert.ToBase64String(actualSha256); + if (expectedSha256Base64 != actualSha256Base64) + { + throw new MSBuildXunitException(result, $"File hashes for {filePath} do not match. Expected {expectedSha256Base64}, Actual {actualSha256Base64}"); + } + } + public static void FileContainsLine(MSBuildResult result, string filePath, string match) { if (result == null) @@ -298,7 +319,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests return filePath; } - public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern) + public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories) { if (result == null) { @@ -319,7 +340,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests if (Directory.Exists(directoryPath)) { - var files = Directory.GetFiles(directoryPath, searchPattern, SearchOption.AllDirectories); + var files = Directory.GetFiles(directoryPath, searchPattern, searchOption); if (files.Length != expected) { throw new FileCountException(result, expected, directoryPath, searchPattern, files); @@ -508,6 +529,44 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } } + public static void AssemblyContainsResource(MSBuildResult result, string assemblyPath, string resourceName) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); + + var resources = GetAssemblyResourceNames(assemblyPath); + Assert.Contains(resourceName, resources); + } + + public static void AssemblyDoesNotContainResource(MSBuildResult result, string assemblyPath, string resourceName) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); + + var resources = GetAssemblyResourceNames(assemblyPath); + Assert.DoesNotContain(resourceName, resources); + } + + private static IEnumerable GetAssemblyResourceNames(string assemblyPath) + { + using var file = File.OpenRead(assemblyPath); + using var peReader = new PEReader(file); + var metadataReader = peReader.GetMetadataReader(); + return metadataReader.ManifestResources.Where(r => !r.IsNil).Select(r => + { + var resource = metadataReader.GetManifestResource(r); + return metadataReader.GetString(resource.Name); + }).ToArray(); + } + public static void AssemblyHasAttribute(MSBuildResult result, string assemblyPath, string fullTypeName) { if (result == null) @@ -538,14 +597,20 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } } - private abstract class MSBuildXunitException : Xunit.Sdk.XunitException + public class MSBuildXunitException : Xunit.Sdk.XunitException { protected MSBuildXunitException(MSBuildResult result) { Result = result; } - protected abstract string Heading { get; } + public MSBuildXunitException(MSBuildResult result, string heading) + { + Result = result; + Heading = heading; + } + + protected virtual string Heading { get; } public MSBuildResult Result { get; } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs index 5a64947f8b..4d1b03af59 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs @@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("SimpleMvc")] public async Task Build_RazorOutputPath_SetToNonDefault() { - var customOutputPath = Path.Combine("bin", Configuration, TargetFramework, "Razor"); + var customOutputPath = Path.Combine("bin", Configuration, Project.TargetFramework, "Razor"); var result = await DotnetMSBuild("Build", $"/p:RazorOutputPath={customOutputPath}"); Assert.BuildPassed(result); @@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("SimpleMvc")] public async Task Build_MvcRazorOutputPath_SetToNonDefault() { - var customOutputPath = Path.Combine("bin", Configuration, TargetFramework, "Razor"); + var customOutputPath = Path.Combine("bin", Configuration, Project.TargetFramework, "Razor"); var result = await DotnetMSBuild("Build", $"/p:MvcRazorOutputPath={customOutputPath}"); Assert.BuildPassed(result); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs index 1fe81b585a..23faba7d40 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs @@ -12,12 +12,10 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { public class BuildServerIntegrationTest : MSBuildIntegrationTestBase, IClassFixture { - private BuildServerTestFixture _buildServer; public BuildServerIntegrationTest(BuildServerTestFixture buildServer) : base(buildServer) { - _buildServer = buildServer; } [Fact] @@ -171,7 +169,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests var toolAssembly = Path.Combine(publishDir, "rzc.dll"); var result = await DotnetMSBuild( "Build", - $"/p:_RazorForceBuildServer=true /p:_RazorToolAssembly={toolAssembly}", + $"/p:_RazorForceBuildServer=true /p:_RazorSdkToolAssembly={toolAssembly}", suppressBuildServer: true); // We don't want to specify a pipe name Assert.BuildPassed(result); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs index 544e98e281..797d244625 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs @@ -20,5 +20,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests public static string RazorSdkDirectoryRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "RazorSdkDirectoryRoot").Value; public static string RepoRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "Testing.RepoRoot").Value; + + public static string DefaultNetCoreTargetFramework => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "DefaultNetCoreTargetFramework").Value; } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponents31IntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponents31IntegrationTest.cs index a3422fe9ea..6c09b10104 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponents31IntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponents31IntegrationTest.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("blazor31")] public async Task Build_Components_WithDotNetCoreMSBuild_Works() { - TargetFramework = "netcoreapp3.1"; + Project.TargetFramework = "netcoreapp3.1"; var result = await DotnetMSBuild("Build"); Assert.BuildPassed(result); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponentsIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponentsIntegrationTest.cs index ce867b6b8d..fa6e78c7d7 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponentsIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponentsIntegrationTest.cs @@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("ComponentLibrary")] public async Task Build_WithoutRazorLangVersion_ProducesWarning() { - TargetFramework = "netstandard2.0"; + Project.TargetFramework = "netstandard2.0"; var result = await DotnetMSBuild("Build", "/p:RazorLangVersion="); Assert.BuildPassed(result, allowWarnings: true); @@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("ComponentLibrary")] public async Task Building_NetstandardComponentLibrary() { - TargetFramework = "netstandard2.0"; + Project.TargetFramework = "netstandard2.0"; // Build var result = await DotnetMSBuild("Build"); @@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("ComponentLibrary")] public async Task Build_DoesNotProduceRefsDirectory() { - TargetFramework = "netstandard2.0"; + Project.TargetFramework = "netstandard2.0"; // Build var result = await DotnetMSBuild("Build"); @@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("ComponentLibrary")] public async Task Publish_DoesNotProduceRefsDirectory() { - TargetFramework = "netstandard2.0"; + Project.TargetFramework = "netstandard2.0"; // Build var result = await DotnetMSBuild("Publish"); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/DesignTimeBuildIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/DesignTimeBuildIntegrationTest.cs index f5baa8b4b9..8576598145 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/DesignTimeBuildIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/DesignTimeBuildIntegrationTest.cs @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("ComponentLibrary")] public async Task RazorGenerateComponentDesignTime_ReturnsRazorComponentWithTargetPath() { - TargetFramework = "netstandard2.0"; + Project.TargetFramework = "netstandard2.0"; var result = await DotnetMSBuild("RazorGenerateComponentDesignTime;_IntrospectRazorComponentWithTargetPath"); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs index 0266ecdd1b..81faa7d2a1 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Security.Cryptography; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests @@ -36,6 +38,24 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests return new FileThumbPrint(path, lastWriteTimeUtc, hash); } + /// + /// Returns a list of thumbprints for all files (recursive) in the specified directory, sorted by file paths. + /// + internal static List CreateFolderThumbprint(ProjectDirectory project, string directoryPath, params string[] filesToIgnore) + { + directoryPath = System.IO.Path.Combine(project.DirectoryPath, directoryPath); + var files = Directory.GetFiles(directoryPath).Where(p => !filesToIgnore.Contains(p)); + var thumbprintLookup = new List(); + foreach (var file in files) + { + var thumbprint = Create(file); + thumbprintLookup.Add(thumbprint); + } + + thumbprintLookup.Sort(Comparer.Create((a, b) => StringComparer.Ordinal.Compare(a.Path, b.Path))); + return thumbprintLookup; + } + public bool Equals(FileThumbPrint other) { return diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/InitializeTestProjectAttribute.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/InitializeTestProjectAttribute.cs index ccc1b06af9..8293d4488a 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/InitializeTestProjectAttribute.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/InitializeTestProjectAttribute.cs @@ -36,12 +36,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests throw new InvalidOperationException($"This should be used on a class derived from {typeof(MSBuildIntegrationTestBase)}"); } - MSBuildIntegrationTestBase.Project = ProjectDirectory.Create(_originalProjectName, _testProjectName, _baseDirectory, _additionalProjects, _language); -#if NETCOREAPP - MSBuildIntegrationTestBase.TargetFramework = "net5.0"; -#else -#error Target frameworks need to be updated -#endif + MSBuildIntegrationTestBase.Project = ProjectDirectory.Create(_originalProjectName, new ProjectDirectory.ProjectDirectoryOptions(_baseDirectory, _testProjectName, _language), _additionalProjects); } public override void After(MethodInfo methodUnderTest) diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildIntegrationTestBase.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildIntegrationTestBase.cs index ff17163390..cc9fd1009a 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildIntegrationTestBase.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildIntegrationTestBase.cs @@ -16,7 +16,6 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests public abstract class MSBuildIntegrationTestBase { private static readonly AsyncLocal _project = new AsyncLocal(); - private static readonly AsyncLocal _projectTfm = new AsyncLocal(); protected MSBuildIntegrationTestBase(BuildServerTestFixtureBase buildServer) { @@ -31,9 +30,9 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests #error Configuration not supported #endif - protected string IntermediateOutputPath => Path.Combine("obj", Configuration, TargetFramework); + protected string IntermediateOutputPath => Path.Combine("obj", Configuration, Project.TargetFramework); - protected string OutputPath => Path.Combine("bin", Configuration, TargetFramework); + protected string OutputPath => Path.Combine("bin", Configuration, Project.TargetFramework); protected string PublishOutputPath => Path.Combine(OutputPath, "publish"); @@ -50,12 +49,6 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests protected string RazorComponentIntermediateOutputPath => Path.Combine(IntermediateOutputPath, "RazorDeclaration"); - internal static string TargetFramework - { - get => _projectTfm.Value; - set => _projectTfm.Value = value; - } - protected BuildServerTestFixtureBase BuildServer { get; set; } internal Task DotnetMSBuild( @@ -111,16 +104,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } internal void AddProjectFileContent(string content) - { - if (content == null) - { - throw new ArgumentNullException(nameof(content)); - } - - var existing = File.ReadAllText(Project.ProjectFilePath); - var updated = existing.Replace("", content); - File.WriteAllText(Project.ProjectFilePath, updated); - } + => Project.AddProjectFileContent(content); internal void ReplaceContent(string content, params string[] paths) { diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs index e9a9216782..5ab553d6bb 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -15,6 +16,47 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { internal static class MSBuildProcessManager { + internal static Task DotnetMSBuild( + ProjectDirectory project, + string target = "Build", + string args = null, + string buildServerPipeName = null) + { + var buildArgumentList = new List + { + // Disable node-reuse. We don't want msbuild processes to stick around + // once the test is completed. + "/nr:false", + + // Always generate a bin log for debugging purposes + "/bl", + + // Let the test app know it is running as part of a test. + "/p:RunningAsTest=true", + + $"/p:MicrosoftNETCoreAppRuntimeVersion={BuildVariables.MicrosoftNETCoreAppRuntimeVersion}", + $"/p:MicrosoftNetCompilersToolsetPackageVersion={BuildVariables.MicrosoftNetCompilersToolsetPackageVersion}", + $"/p:RazorSdkDirectoryRoot={BuildVariables.RazorSdkDirectoryRoot}", + $"/p:RepoRoot={BuildVariables.RepoRoot}", + $"/p:Configuration={project.Configuration}", + $"/t:{target}", + args, + }; + + if (buildServerPipeName != null) + { + buildArgumentList.Add($@"/p:_RazorBuildServerPipeName=""{buildServerPipeName}"""); + } + + var buildArguments = string.Join(" ", buildArgumentList); + + return RunProcessAsync( + project, + buildArguments, + timeout: null, + MSBuildProcessKind.Dotnet); + } + public static async Task RunProcessAsync( ProjectDirectory project, string arguments, 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 a28e8d3e91..a1e160322e 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 @@ -50,6 +50,11 @@ <_Parameter2>$(MicrosoftNETCoreAppRuntimeVersion) + + <_Parameter1>DefaultNetCoreTargetFramework + <_Parameter2>$(DefaultNetCoreTargetFramework) + + <_Parameter1>MicrosoftNetCompilersToolsetPackageVersion <_Parameter2>$(MicrosoftNetCompilersToolsetPackageVersion) @@ -72,6 +77,11 @@ + + + + + diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MvcBuildIntegrationTestLegacy.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MvcBuildIntegrationTestLegacy.cs index dbc85428aa..f73bdeb2d6 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MvcBuildIntegrationTestLegacy.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MvcBuildIntegrationTestLegacy.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests IClassFixture { public abstract string TestProjectName { get; } - public abstract new string TargetFramework { get; } + public abstract string TargetFramework { get; } public virtual string OutputFileName => $"{TestProjectName}.dll"; public MvcBuildIntegrationTestLegacy(LegacyBuildServerTestFixture buildServer) @@ -24,8 +24,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests protected IDisposable CreateTestProject() { - Project = ProjectDirectory.Create(TestProjectName, TestProjectName, string.Empty, Array.Empty(), "C#"); - MSBuildIntegrationTestBase.TargetFramework = TargetFramework; + Project = ProjectDirectory.Create(TestProjectName); + Project.TargetFramework = TargetFramework; return new Disposable(); } @@ -52,7 +52,6 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests result, Path.Combine(IntermediateOutputPath, $"{TestProjectName}.TagHelpers.output.cache"), @"""Name"":""SimpleMvc.SimpleTagHelper"""); - } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs index dfe9c90f48..6b0688031c 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs @@ -258,7 +258,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("ComponentLibrary")] public async Task Pack_DoesNotIncludeAnyCustomPropsFiles_WhenNoStaticAssetsAreAvailable() { - MSBuildIntegrationTestBase.TargetFramework = "netstandard2.0"; + Project.TargetFramework = "netstandard2.0"; var result = await DotnetMSBuild("Pack"); @@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests [InitializeTestProject("PackageLibraryTransitiveDependency")] public async Task Pack_Incremental_DoesNotRegenerateCacheAndPropsFiles() { - TargetFramework = "netstandard2.0"; + Project.TargetFramework = "netstandard2.0"; var result = await DotnetMSBuild("Pack"); Assert.BuildPassed(result, allowWarnings: true); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs index e2a28a1b06..d0d0e571f4 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; -using Microsoft.AspNetCore.Testing; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { @@ -18,9 +17,28 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests public bool PreserveWorkingDirectory { get; set; } #endif - public static ProjectDirectory Create(string originalProjectName, string targetProjectName, string baseDirectory, string[] additionalProjects, string language) + public readonly struct ProjectDirectoryOptions { - var destinationPath = Path.Combine(Path.GetTempPath(), "Razor", baseDirectory, Path.GetRandomFileName()); + public ProjectDirectoryOptions(string baseDirectory, string targetProjectName, string language) + { + BaseDirectory = baseDirectory; + TargetProjectName = targetProjectName; + Language = language; + } + + public string TargetProjectName { get; } + + public string BaseDirectory { get; } + + public string Language { get; } + } + + public static ProjectDirectory Create(string projectName, params string[] additionalProjects) => Create(projectName, default, additionalProjects); + + public static ProjectDirectory Create(string originalProjectName, ProjectDirectoryOptions options, params string[] additionalProjects) + { + // string targetProjectName, string baseDirectory, + var destinationPath = Path.Combine(Path.GetTempPath(), "Razor", options.BaseDirectory ?? string.Empty, Path.GetRandomFileName()); Directory.CreateDirectory(destinationPath); try @@ -50,30 +68,27 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } // Rename the csproj/fsproj - string extension; - if (language.Equals("C#", StringComparison.OrdinalIgnoreCase)) - { - extension = ".csproj"; - } - else if (language.Equals("F#", StringComparison.OrdinalIgnoreCase)) + var extension = ".csproj"; + if (string.Equals("F#", options.Language, StringComparison.OrdinalIgnoreCase)) { extension = ".fsproj"; } - else - { - throw new InvalidOperationException($"Language {language} is not supported."); - } + var directoryPath = Path.Combine(destinationPath, originalProjectName); - var oldProjectFilePath = Path.Combine(directoryPath, originalProjectName + extension); - var newProjectFilePath = Path.Combine(directoryPath, targetProjectName + extension); - File.Move(oldProjectFilePath, newProjectFilePath); + var projectFilePath = Path.Combine(directoryPath, originalProjectName + extension); + if (options.TargetProjectName != null) + { + var newProjectFilePath = Path.Combine(directoryPath, options.TargetProjectName + extension); + File.Move(projectFilePath, newProjectFilePath); + projectFilePath = newProjectFilePath; + } CopyRepositoryAssets(repositoryRoot, destinationPath); return new ProjectDirectory( destinationPath, directoryPath, - newProjectFilePath); + projectFilePath); } catch { @@ -127,7 +142,7 @@ $@" "; File.WriteAllText(Path.Combine(projectDestination, "Before.Directory.Build.props"), beforeDirectoryPropsContent); - new List { "Directory.Build.props", "Directory.Build.targets", "RazorTest.Introspection.targets" } + new List { "Directory.Build.props", "Directory.Build.targets", "RazorTest.Introspection.targets", "blazor.webassembly.js" } .ForEach(file => { var source = Path.Combine(testAppsRoot, file); @@ -149,6 +164,12 @@ $@" } } + public ProjectDirectory GetSibling(string projectName) + { + var siblingDirectory = Path.GetFullPath(Path.Combine(DirectoryPath, "..", projectName)); + return new ProjectDirectory(SolutionPath, siblingDirectory, Path.Combine(siblingDirectory, projectName + ".csproj")); + } + protected ProjectDirectory(string solutionPath, string directoryPath, string projectFilePath) { SolutionPath = solutionPath; @@ -156,12 +177,38 @@ $@" ProjectFilePath = projectFilePath; } + public string TargetFramework { get; set; } = BuildVariables.DefaultNetCoreTargetFramework; + + public string RuntimeIdentifier { get; set; } = string.Empty; + + public string Configuration { get; set; } = +#if DEBUG + "Debug"; +#else + "Release"; +#endif + + /// + /// Razor-Temp\unique-id\project + /// public string DirectoryPath { get; } + /// + /// Razor-Temp\unique-id\project\project.csproj + /// public string ProjectFilePath { get;} + /// + /// Razor-Temp\unique-id\ + /// public string SolutionPath { get; } + public string IntermediateOutputDirectory => Path.Combine("obj", Configuration, TargetFramework, RuntimeIdentifier); + + public string BuildOutputDirectory => Path.Combine("bin", Configuration, TargetFramework, RuntimeIdentifier); + + public string PublishOutputDirectory => Path.Combine(BuildOutputDirectory, "publish"); + public void Dispose() { if (PreserveWorkingDirectory) @@ -210,5 +257,31 @@ $@" throw new Exception($"File {fileName} could not be found in {baseDirectory} or its parent directories."); } + + internal void AddProjectFileContent(string content) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + var existing = File.ReadAllText(ProjectFilePath); + var updated = existing.Replace("", content); + File.WriteAllText(ProjectFilePath, updated); + } + + internal void AddDirectoryBuildContent(string content) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + var filepath = Path.Combine(DirectoryPath, "Directory.Build.props"); + + var existing = File.ReadAllText(filepath); + var updated = existing.Replace("", content); + File.WriteAllText(filepath, updated); + } } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs new file mode 100644 index 0000000000..7585b5b76c --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs @@ -0,0 +1,80 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using Microsoft.AspNetCore.Razor.Tasks; + +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests +{ + internal static class ServiceWorkerAssert + { + internal static void VerifyServiceWorkerFiles(MSBuildResult result, string outputDirectory, string serviceWorkerPath, string serviceWorkerContent, string assetsManifestPath) + { + // Check the expected files are there + var serviceWorkerResolvedPath = Assert.FileExists(result, outputDirectory, serviceWorkerPath); + var assetsManifestResolvedPath = Assert.FileExists(result, outputDirectory, assetsManifestPath); + + // Check the service worker contains the expected content (which comes from the PublishedContent file) + Assert.FileContains(result, serviceWorkerResolvedPath, serviceWorkerContent); + + // Check the assets manifest version was added to the published service worker + var assetsManifest = ReadServiceWorkerAssetsManifest(assetsManifestResolvedPath); + Assert.FileContains(result, serviceWorkerResolvedPath, $"/* Manifest version: {assetsManifest.version} */"); + + // Check the assets manifest contains correct entries for all static content we're publishing + var resolvedPublishDirectory = Path.Combine(result.Project.DirectoryPath, outputDirectory); + var outputFiles = Directory.GetFiles(resolvedPublishDirectory, "*", new EnumerationOptions { RecurseSubdirectories = true }); + var assetsManifestHashesByUrl = (IReadOnlyDictionary)assetsManifest.assets.ToDictionary(x => x.url, x => x.hash); + foreach (var filePath in outputFiles) + { + var relativePath = Path.GetRelativePath(resolvedPublishDirectory, filePath); + + // We don't list compressed files in the SWAM, as these are transparent to the client, + // nor do we list the service worker itself or its assets manifest, as these don't need to be fetched in the same way + if (IsCompressedFile(relativePath) + || string.Equals(relativePath, serviceWorkerPath, StringComparison.Ordinal) + || string.Equals(relativePath, assetsManifestPath, StringComparison.Ordinal)) + { + continue; + } + + // Verify hash + var fileUrl = relativePath.Replace('\\', '/'); + var expectedHash = ParseWebFormattedHash(assetsManifestHashesByUrl[fileUrl]); + Assert.Contains(fileUrl, assetsManifestHashesByUrl); + Assert.FileHashEquals(result, filePath, expectedHash); + } + } + + private static string ParseWebFormattedHash(string webFormattedHash) + { + Assert.StartsWith("sha256-", webFormattedHash); + return webFormattedHash.Substring(7); + } + + private static bool IsCompressedFile(string path) + { + switch (Path.GetExtension(path)) + { + case ".br": + case ".gz": + return true; + default: + return false; + } + } + + private static AssetsManifestFile ReadServiceWorkerAssetsManifest(string assetsManifestResolvedPath) + { + var jsContents = File.ReadAllText(assetsManifestResolvedPath); + var jsonStart = jsContents.IndexOf("{"); + var jsonLength = jsContents.LastIndexOf("}") - jsonStart + 1; + var json = jsContents.Substring(jsonStart, jsonLength); + return JsonSerializer.Deserialize(json); + } + } +} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIncrementalismTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs similarity index 72% rename from src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIncrementalismTest.cs rename to src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs index 46a85cdea1..301d688e40 100644 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIncrementalismTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs @@ -4,17 +4,18 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Tasks; using Xunit; -namespace Microsoft.AspNetCore.Components.WebAssembly.Build +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { - public class BuildIncrementalismTest + public class WasmBuildIncrementalismTest { [Fact] public async Task Build_WithLinker_IsIncremental() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); var result = await MSBuildProcessManager.DotnetMSBuild(project); Assert.BuildPassed(result); @@ -43,14 +44,14 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build public async Task Build_SatelliteAssembliesFileIsPreserved() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); File.Move(Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt"), Path.Combine(project.DirectoryPath, "Resource.ja.resx")); var result = await MSBuildProcessManager.DotnetMSBuild(project); Assert.BuildPassed(result); - var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor", "blazor.satelliteasm.props"); - var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "ja", "standalone.resources.dll"); + var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor.satelliteasm.props"); + var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "ja", "blazorwasm.resources.dll"); var bootJson = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); // Assert @@ -76,11 +77,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, satelliteAssemblyCacheFile); Assert.FileExists(result, satelliteAssemblyFile); - var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); var satelliteResources = bootJsonFile.resources.satelliteResources; var kvp = Assert.Single(satelliteResources); Assert.Equal("ja", kvp.Key); - Assert.Equal("ja/standalone.resources.dll", Assert.Single(kvp.Value).Key); + Assert.Equal("ja/blazorwasm.resources.dll", Assert.Single(kvp.Value).Key); } } @@ -88,13 +89,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build public async Task Build_SatelliteAssembliesFileIsCreated_IfNewFileIsAdded() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); var result = await MSBuildProcessManager.DotnetMSBuild(project); Assert.BuildPassed(result); - var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor", "blazor.satelliteasm.props"); - var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "ja", "standalone.resources.dll"); + var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor.satelliteasm.props"); + var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "ja", "blazorwasm.resources.dll"); var bootJson = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); result = await MSBuildProcessManager.DotnetMSBuild(project); @@ -102,7 +103,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileDoesNotExist(result, satelliteAssemblyCacheFile); Assert.FileDoesNotExist(result, satelliteAssemblyFile); - var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); var satelliteResources = bootJsonFile.resources.satelliteResources; Assert.Null(satelliteResources); @@ -112,26 +113,26 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, satelliteAssemblyCacheFile); Assert.FileExists(result, satelliteAssemblyFile); - bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); satelliteResources = bootJsonFile.resources.satelliteResources; var kvp = Assert.Single(satelliteResources); Assert.Equal("ja", kvp.Key); - Assert.Equal("ja/standalone.resources.dll", Assert.Single(kvp.Value).Key); + Assert.Equal("ja/blazorwasm.resources.dll", Assert.Single(kvp.Value).Key); } [Fact] public async Task Build_SatelliteAssembliesFileIsDeleted_IfAllSatelliteFilesAreRemoved() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); File.Move(Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt"), Path.Combine(project.DirectoryPath, "Resource.ja.resx")); var result = await MSBuildProcessManager.DotnetMSBuild(project); Assert.BuildPassed(result); - var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor", "blazor.satelliteasm.props"); - var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "ja", "standalone.resources.dll"); + var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor.satelliteasm.props"); + var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "ja", "blazorwasm.resources.dll"); var bootJson = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); result = await MSBuildProcessManager.DotnetMSBuild(project); @@ -139,11 +140,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, satelliteAssemblyCacheFile); Assert.FileExists(result, satelliteAssemblyFile); - var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); var satelliteResources = bootJsonFile.resources.satelliteResources; var kvp = Assert.Single(satelliteResources); Assert.Equal("ja", kvp.Key); - Assert.Equal("ja/standalone.resources.dll", Assert.Single(kvp.Value).Key); + Assert.Equal("ja/blazorwasm.resources.dll", Assert.Single(kvp.Value).Key); File.Delete(Path.Combine(project.DirectoryPath, "Resource.ja.resx")); @@ -151,7 +152,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.BuildPassed(result); Assert.FileDoesNotExist(result, satelliteAssemblyCacheFile); - bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); satelliteResources = bootJsonFile.resources.satelliteResources; Assert.Null(satelliteResources); } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs new file mode 100644 index 0000000000..8788d9ce17 --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs @@ -0,0 +1,242 @@ +// 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.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.AspNetCore.Testing; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests +{ + public class WasmBuildIntegrationTest + { + private static readonly string DotNetJsFileName = $"dotnet.{BuildVariables.MicrosoftNETCoreAppRuntimeVersion}.js"; + + [Fact] + public async Task Build_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.pdb"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.pdb"); + + var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm.StaticWebAssets.xml"); + Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot")); + Assert.FileContains(result, staticWebAssets, Path.GetFullPath(Path.Combine(project.SolutionPath, "razorclasslibrary", "wwwroot"))); + } + + [Fact] + public async Task Build_InRelease_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Release"; + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.pdb"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.pdb"); + + var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm.StaticWebAssets.xml"); + Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot")); + Assert.FileContains(result, staticWebAssets, Path.GetFullPath(Path.Combine(project.SolutionPath, "razorclasslibrary", "wwwroot"))); + } + + [Fact] + public async Task Build_ProducesBootJsonDataWithExpectedContent() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Debug"; + var wwwroot = Path.Combine(project.DirectoryPath, "wwwroot"); + File.WriteAllText(Path.Combine(wwwroot, "appsettings.json"), "Default settings"); + File.WriteAllText(Path.Combine(wwwroot, "appsettings.development.json"), "Development settings"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + var bootJsonData = ReadBootJsonData(result, bootJsonPath); + + var runtime = bootJsonData.resources.runtime.Keys; + Assert.Contains(DotNetJsFileName, runtime); + Assert.Contains("dotnet.wasm", runtime); + + var assemblies = bootJsonData.resources.assembly.Keys; + Assert.Contains("blazorwasm.dll", assemblies); + Assert.Contains("RazorClassLibrary.dll", assemblies); + Assert.Contains("System.Text.Json.dll", assemblies); + + var pdb = bootJsonData.resources.pdb.Keys; + Assert.Contains("blazorwasm.pdb", pdb); + Assert.Contains("RazorClassLibrary.pdb", pdb); + + Assert.Null(bootJsonData.resources.satelliteResources); + + Assert.Contains("appsettings.json", bootJsonData.config); + Assert.Contains("appsettings.development.json", bootJsonData.config); + } + + [Fact] + public async Task Build_InRelease_ProducesBootJsonDataWithExpectedContent() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Release"; + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + var bootJsonData = ReadBootJsonData(result, bootJsonPath); + + var runtime = bootJsonData.resources.runtime.Keys; + Assert.Contains(DotNetJsFileName, runtime); + Assert.Contains("dotnet.wasm", runtime); + + var assemblies = bootJsonData.resources.assembly.Keys; + Assert.Contains("blazorwasm.dll", assemblies); + Assert.Contains("RazorClassLibrary.dll", assemblies); + Assert.Contains("System.Text.Json.dll", assemblies); + + var pdb = bootJsonData.resources.pdb.Keys; + Assert.Contains("blazorwasm.pdb", pdb); + Assert.Contains("RazorClassLibrary.pdb", pdb); + Assert.Null(bootJsonData.resources.satelliteResources); + } + + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/22975")] + public async Task Build_WithBlazorEnableTimeZoneSupportDisabled_DoesNotCopyTimeZoneInfo() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Release"; + project.AddProjectFileContent( +@" + + false +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + var bootJsonData = ReadBootJsonData(result, bootJsonPath); + + var runtime = bootJsonData.resources.runtime.Keys; + Assert.Contains("dotnet.wasm", runtime); + Assert.DoesNotContain("dotnet.timezones.dat", runtime); + + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm"); + Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.timezones.dat"); + } + + [Fact] + public async Task Build_Hosted_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "blazorwasm.dll"); + + var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorhosted.StaticWebAssets.xml"); + Assert.FileContains(result, staticWebAssets, Path.Combine("net5.0", "wwwroot")); + Assert.FileContains(result, staticWebAssets, Path.Combine("razorclasslibrary", "wwwroot")); + Assert.FileContains(result, staticWebAssets, Path.Combine("blazorwasm", "wwwroot")); + } + + [Fact] + [QuarantinedTest] + public async Task Build_SatelliteAssembliesAreCopiedToBuildOutput() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" }); + project.AddProjectFileContent( +@" + + $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies + + + +"); + var resxfileInProject = Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt"); + File.Move(resxfileInProject, Path.Combine(project.DirectoryPath, "Resource.ja.resx")); + + var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore"); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "classlibrarywithsatelliteassemblies.dll"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "Microsoft.CodeAnalysis.CSharp.dll"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output. + + var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\""); + Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\""); + } + + [Fact] + public async Task Build_WithCustomOutputPath_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + + project.AddDirectoryBuildContent( +@" + $(MSBuildThisFileDirectory)build\bin\ + $(MSBuildThisFileDirectory)build\obj\ +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore"); + Assert.BuildPassed(result); + } + + private static BootJsonData ReadBootJsonData(MSBuildResult result, string path) + { + return JsonSerializer.Deserialize( + File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)), + new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + } +} diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs new file mode 100644 index 0000000000..674d180c88 --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs @@ -0,0 +1,173 @@ +// 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.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests +{ + public class WasmBuildLazyLoadTest + { + [Fact] + public async Task Build_LazyLoadExplicitAssembly_Debug_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Debug"; + + project.AddProjectFileContent( +@" + + + +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + var buildOutputDirectory = project.BuildOutputDirectory; + + // Verify that a blazor.boot.json file has been created + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + // And that the assembly is in the output + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll"); + + var bootJson = ReadBootJsonData(result, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json")); + + // And that it has been labelled as a dynamic assembly in the boot.json + var dynamicAssemblies = bootJson.resources.dynamicAssembly; + var assemblies = bootJson.resources.assembly; + + Assert.NotNull(dynamicAssemblies); + Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys); + Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); + + // App assembly should not be lazy loaded + Assert.DoesNotContain("blazorwasm.dll", dynamicAssemblies.Keys); + Assert.Contains("blazorwasm.dll", assemblies.Keys); + } + + [Fact] + public async Task Build_LazyLoadExplicitAssembly_Release_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Release"; + + project.AddProjectFileContent( +@" + + + +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + var buildOutputDirectory = project.BuildOutputDirectory; + + // Verify that a blazor.boot.json file has been created + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + // And that the assembly is in the output + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll"); + + var bootJson = ReadBootJsonData(result, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json")); + + // And that it has been labelled as a dynamic assembly in the boot.json + var dynamicAssemblies = bootJson.resources.dynamicAssembly; + var assemblies = bootJson.resources.assembly; + + Assert.NotNull(dynamicAssemblies); + Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys); + Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); + + // App assembly should not be lazy loaded + Assert.DoesNotContain("blazorwasm.dll", dynamicAssemblies.Keys); + Assert.Contains("blazorwasm.dll", assemblies.Keys); + } + + [Fact] + public async Task Publish_LazyLoadExplicitAssembly_Debug_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Debug"; + + project.AddProjectFileContent( +@" + + + +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + + var publishDirectory = project.PublishOutputDirectory; + + // Verify that a blazor.boot.json file has been created + Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "blazor.boot.json"); + // And that the assembly is in the output + Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll"); + + var bootJson = ReadBootJsonData(result, Path.Combine(publishDirectory, "wwwroot", "_framework", "blazor.boot.json")); + + // And that it has been labelled as a dynamic assembly in the boot.json + var dynamicAssemblies = bootJson.resources.dynamicAssembly; + var assemblies = bootJson.resources.assembly; + + Assert.NotNull(dynamicAssemblies); + Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys); + Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); + + // App assembly should not be lazy loaded + Assert.DoesNotContain("blazorwasm.dll", dynamicAssemblies.Keys); + Assert.Contains("blazorwasm.dll", assemblies.Keys); + } + + [Fact] + public async Task Publish_LazyLoadExplicitAssembly_Release_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Release"; + + project.AddProjectFileContent( +@" + + + +"); + + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + + var publishDirectory = project.PublishOutputDirectory; + + // Verify that a blazor.boot.json file has been created + Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "blazor.boot.json"); + // And that the assembly is in the output + Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll"); + + var bootJson = ReadBootJsonData(result, Path.Combine(publishDirectory, "wwwroot", "_framework", "blazor.boot.json")); + + // And that it has been labelled as a dynamic assembly in the boot.json + var dynamicAssemblies = bootJson.resources.dynamicAssembly; + var assemblies = bootJson.resources.assembly; + + Assert.NotNull(dynamicAssemblies); + Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys); + Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); + + // App assembly should not be lazy loaded + Assert.DoesNotContain("blazorwasm.dll", dynamicAssemblies.Keys); + Assert.Contains("blazorwasm.dll", assemblies.Keys); + } + + private static BootJsonData ReadBootJsonData(MSBuildResult result, string path) + { + return JsonSerializer.Deserialize( + File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)), + new JsonSerializerOptions(JsonSerializerDefaults.Web)); + } + } +} diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs new file mode 100644 index 0000000000..e308d78274 --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs @@ -0,0 +1,165 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests +{ + public class WasmCompressionTests + { + [Fact] + public async Task Publish_UpdatesFilesWhenSourcesChange() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); + + Assert.BuildPassed(result); + + // Act + var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll"); + var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll.br"); + var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + var blazorBootJson = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + var blazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); + var blazorBootJsonCompressed = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json.br"); + var blazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); + + var programFile = Path.Combine(project.DirectoryPath, "..", "blazorwasm", "Program.cs"); + var programFileContents = File.ReadAllText(programFile); + File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); + + // Assert + result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); + Assert.BuildPassed(result); + var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + var newBlazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); + var newBlazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); + + Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); + Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); + + Assert.NotEqual(blazorBootJsonThumbPrint, newBlazorBootJsonThumbPrint); + Assert.NotEqual(blazorBootJsonCompressedThumbPrint, newBlazorBootJsonCompressedThumbPrint); + } + + [Fact] + public async Task Publish_WithoutLinkerAndCompression_UpdatesFilesWhenSourcesChange() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false"); + + Assert.BuildPassed(result); + + // Act + var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll"); + var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + + var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll.br"); + var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + var programFile = Path.Combine(project.DirectoryPath, "..", "blazorwasm", "Program.cs"); + var programFileContents = File.ReadAllText(programFile); + File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); + + // Assert + result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false"); + Assert.BuildPassed(result); + var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); + Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); + } + + [Fact] + public async Task Publish_WithLinkerAndCompression_IsIncremental() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + // Act + var compressedFilesFolder = Path.Combine("..", "blazorwasm", project.IntermediateOutputDirectory, "brotli"); + var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); + + // Assert + for (var i = 0; i < 3; i++) + { + result = await MSBuildProcessManager.DotnetMSBuild(project); + Assert.BuildPassed(result); + + var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); + Assert.Equal(thumbPrint.Count, newThumbPrint.Count); + for (var j = 0; j < thumbPrint.Count; j++) + { + Assert.Equal(thumbPrint[j], newThumbPrint[j]); + } + } + } + + [Fact] + public async Task Publish_WithoutLinkerAndCompression_IsIncremental() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false"); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + // Act + var compressedFilesFolder = Path.Combine("..", "blazorwasm", project.IntermediateOutputDirectory, "brotli"); + var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); + + // Assert + for (var i = 0; i < 3; i++) + { + result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false"); + Assert.BuildPassed(result); + + var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder); + Assert.Equal(thumbPrint.Count, newThumbPrint.Count); + for (var j = 0; j < thumbPrint.Count; j++) + { + Assert.Equal(thumbPrint[j], newThumbPrint[j]); + } + } + } + + [Fact] + public async Task Publish_CompressesAllFrameworkFiles() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); + + Assert.BuildPassed(result); + + var extensions = new[] { ".dll", ".js", ".pdb", ".wasm", ".map", ".json", ".dat" }; + + // Act + var frameworkFilesPath = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework"); + + foreach (var file in Directory.EnumerateFiles(frameworkFilesPath, "*", new EnumerationOptions { RecurseSubdirectories = true, })) + { + var extension = Path.GetExtension(file); + if (extension != ".br" && extension != ".gz") + { + Assert.FileExists(result, file + ".br"); + } + } + } + } +} diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs similarity index 65% rename from src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs rename to src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs index b4b34470ea..dfaec7c803 100644 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs @@ -1,28 +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.Collections.Generic; using System.IO; -using System.Linq; +using System.IO.Compression; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Blazor.Build; +using Microsoft.AspNetCore.Razor.Tasks; using Microsoft.AspNetCore.Testing; using Xunit; using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary; -using static Microsoft.AspNetCore.Components.WebAssembly.Build.WebAssemblyRuntimePackage; -using System.IO.Compression; +using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert; -namespace Microsoft.AspNetCore.Components.WebAssembly.Build +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { - public class PublishIntegrationTest + public class WasmPublishIntegrationTest { + private static readonly string DotNetJsFileName = $"dotnet.{BuildVariables.MicrosoftNETCoreAppRuntimeVersion}.js"; + [Fact] public async Task Publish_WithDefaultSettings_Works() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new [] { "razorclasslibrary", "LinkBaseToWebRoot" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" }); project.Configuration = "Debug"; var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); @@ -34,10 +33,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. // Verify referenced static web assets Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js"); @@ -61,31 +60,29 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), serviceWorkerContent: "// This is the production service worker", assetsManifestPath: "custom-service-worker-assets.js"); - - // When publishing without linker, we expect to have collation information present in mscorlib.dll - Assert.AssemblyContainsResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin"); } [Fact] public async Task Publish_InRelease_Works() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new [] { "razorclasslibrary", "LinkBaseToWebRoot" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" }); project.Configuration = "Release"; - var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", ""); Assert.BuildPassed(result); + var publishDirectory = project.PublishOutputDirectory; var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. // Verify referenced static web assets Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js"); @@ -103,38 +100,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build // Verify web.config Assert.FileExists(result, publishDirectory, "web.config"); Assert.FileCountEquals(result, 1, publishDirectory, "*", SearchOption.TopDirectoryOnly); - - // When publishing with linker, we expect to have collation information present in mscorlib.dll - Assert.AssemblyContainsResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin"); - } - - [Fact] - public async Task Publish_LinkerEnabledAndBlazorWebAssemblyPreserveCollationDataSet_CollationInformationIsPreserved() - { - // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); - project.Configuration = "Release"; - project.AddProjectFileContent( -@" - false -"); - var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); - - Assert.BuildPassed(result); - - var publishDirectory = project.PublishOutputDirectory; - - var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); - - // When publishing with BlazorWebAssemblyPreserveCollationData=false, collation information should be stripped out. - Assert.AssemblyDoesNotContainResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin"); } [Fact] public async Task Publish_WithExistingWebConfig_Works() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" }); project.Configuration = "Release"; var webConfigContents = "test webconfig contents"; @@ -155,7 +127,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build public async Task Publish_WithNoBuild_Works() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build"); Assert.BuildPassed(result); @@ -169,10 +141,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. // Verify static assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "index.html"); @@ -209,13 +181,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build } [Fact] - public async Task Publish_WithLinkOnBuildDisabled_Works() + public async Task Publish_WithTrimmingdDisabled_Works() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new [] { "razorclasslibrary" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); project.AddProjectFileContent( @" - false + false "); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); @@ -227,10 +199,14 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. + + // Verify compression works + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); // // Verify static assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "index.html"); @@ -253,7 +229,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build public async Task Publish_SatelliteAssemblies_AreCopiedToBuildOutput() { // Arrange - using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" }); + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" }); project.AddProjectFileContent( @" @@ -270,49 +246,44 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build var publishDirectory = project.PublishOutputDirectory; var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.CodeAnalysis.CSharp.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "Microsoft.CodeAnalysis.CSharp.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output. var bootJsonPath = Path.Combine(blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\""); Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\""); VerifyBootManifestHashes(result, blazorPublishDirectory); - VerifyServiceWorkerFiles(result, blazorPublishDirectory, - serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), - serviceWorkerContent: "// This is the production service worker", - assetsManifestPath: "custom-service-worker-assets.js"); } [Fact] [QuarantinedTest] - public async Task Publish_HostedApp_WithLinkOnBuildTrue_Works() + public async Task Publish_HostedApp_DefaultSettings_Works() { // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", }); - project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework; + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); - AddSiblingProjectFileContent(project, @" - - true -"); - Assert.BuildPassed(result); var publishDirectory = project.PublishOutputDirectory; // Make sure the main project exists Assert.FileExists(result, publishDirectory, "blazorhosted.dll"); + + // Verification for https://github.com/dotnet/aspnetcore/issues/19926. Verify binaries for projects + // referenced by the Hosted project appear in the publish directory Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll"); + Assert.FileExists(result, publishDirectory, "blazorwasm.dll"); var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. + // Verify project references appear as static web assets - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "RazorClassLibrary.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll"); // Also verify project references to the server project appear in the publish output Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll"); @@ -327,6 +298,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, publishDirectory, "web.config"); VerifyBootManifestHashes(result, blazorPublishDirectory); + + // Verify compression works + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); + VerifyServiceWorkerFiles(result, blazorPublishDirectory, serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), serviceWorkerContent: "// This is the production service worker", @@ -334,17 +312,82 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build } [Fact] - // Regression test for https://github.com/dotnet/aspnetcore/issues/18752 - public async Task Publish_HostedApp_WithLinkOnBuildFalse_Works() + public async Task Publish_HostedApp_ProducesBootJsonDataWithExpectedContent() { // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", }); - project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework; + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); + var wwwroot = Path.Combine(project.DirectoryPath, "..", "blazorwasm", "wwwroot"); + File.WriteAllText(Path.Combine(wwwroot, "appsettings.json"), "Default settings"); + File.WriteAllText(Path.Combine(wwwroot, "appsettings.development.json"), "Development settings"); - AddSiblingProjectFileContent(project, @" + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + Assert.BuildPassed(result); + + var bootJsonPath = Path.Combine(project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + var bootJsonData = ReadBootJsonData(result, bootJsonPath); + + var runtime = bootJsonData.resources.runtime.Keys; + Assert.Contains(DotNetJsFileName, runtime); + Assert.Contains("dotnet.wasm", runtime); + + var assemblies = bootJsonData.resources.assembly.Keys; + Assert.Contains("blazorwasm.dll", assemblies); + Assert.Contains("RazorClassLibrary.dll", assemblies); + Assert.Contains("System.Text.Json.dll", assemblies); + + // No pdbs + Assert.Null(bootJsonData.resources.pdb); + Assert.Null(bootJsonData.resources.satelliteResources); + + Assert.Contains("appsettings.json", bootJsonData.config); + Assert.Contains("appsettings.development.json", bootJsonData.config); + } + + [Fact] + public async Task Publish_HostedApp_WithSatelliteAssemblies() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", "classlibrarywithsatelliteassemblies", }); + var wasmProject = project.GetSibling("blazorwasm"); + + wasmProject.AddProjectFileContent( +@" - false -"); + $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies + + + +"); + var resxfileInProject = Path.Combine(wasmProject.DirectoryPath, "Resources.ja.resx.txt"); + File.Move(resxfileInProject, Path.Combine(wasmProject.DirectoryPath, "Resource.ja.resx")); + + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/restore"); + Assert.BuildPassed(result); + + var bootJsonPath = Path.Combine(project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + var bootJsonData = ReadBootJsonData(result, bootJsonPath); + + var publishOutputDirectory = project.PublishOutputDirectory; + + Assert.FileExists(result, publishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll"); + Assert.FileExists(result, publishOutputDirectory, "wwwroot", "_framework", "classlibrarywithsatelliteassemblies.dll"); + Assert.FileExists(result, publishOutputDirectory, "wwwroot", "_framework", "Microsoft.CodeAnalysis.CSharp.dll"); + Assert.FileExists(result, publishOutputDirectory, "wwwroot", "_framework", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output. + + Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\""); + Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\""); + } + + [Fact] + // Regression test for https://github.com/dotnet/aspnetcore/issues/18752 + public async Task Publish_HostedApp_WithoutTrimming_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); + AddWasmProjectContent(project, @" + + false + "); // VS builds projects individually and then a publish with BuildDependencies=false, but building the main project is a close enough approximation for this test. var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build"); @@ -361,16 +404,19 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build // Verification for https://github.com/dotnet/aspnetcore/issues/19926. Verify binaries for projects // referenced by the Hosted project appear in the publish directory Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll"); - Assert.FileExists(result, publishDirectory, "standalone.dll"); + Assert.FileExists(result, publishDirectory, "blazorwasm.dll"); var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "RazorClassLibrary.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. + + // Verify project references appear as static web assets + Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll"); + // Also verify project references to the server project appear in the publish output + Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll"); // Verify static assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "index.html"); @@ -379,26 +425,28 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js"); Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css"); - // Verify static assets are in the publish directory - Assert.FileExists(result, blazorPublishDirectory, "index.html"); - // Verify web.config Assert.FileExists(result, publishDirectory, "web.config"); VerifyBootManifestHashes(result, blazorPublishDirectory); + + // Verify compression works + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); + VerifyServiceWorkerFiles(result, blazorPublishDirectory, serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), serviceWorkerContent: "// This is the production service worker", assetsManifestPath: "custom-service-worker-assets.js"); - - } + } [Fact] public async Task Publish_HostedApp_WithNoBuild_Works() { // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", }); - project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework; + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build"); Assert.BuildPassed(result); @@ -411,11 +459,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. // Verify static assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "index.html"); @@ -439,8 +486,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build { // Simulates publishing the same way VS does by setting BuildProjectReferences=false. // Arrange - using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", }); - project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework; + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); project.Configuration = "Release"; var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build", "/p:BuildInsideVisualStudio=true"); @@ -454,11 +500,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. // Verify static assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "index.html"); @@ -467,6 +512,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js"); Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css"); + // Verify compression works + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); + // Verify web.config Assert.FileExists(result, publishDirectory, "web.config"); @@ -484,26 +535,24 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build { // Simulates publishing the same way VS does by setting BuildProjectReferences=false. // Arrange - var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", "classlibrarywithsatelliteassemblies" }); - project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework; - project.Configuration = "Release"; - var standaloneProjectDirectory = Path.Combine(project.DirectoryPath, "..", "standalone"); + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", "classlibrarywithsatelliteassemblies" }); + var blazorwasmProjectDirectory = Path.Combine(project.DirectoryPath, "..", "blazorwasm"); - // Setup the standalone app to have it's own satellite assemblies as well as transitively imported satellite assemblies. - var resxfileInProject = Path.Combine(standaloneProjectDirectory , "Resources.ja.resx.txt"); - File.Move(resxfileInProject, Path.Combine(standaloneProjectDirectory, "Resource.ja.resx")); + // Setup the blazorwasm app to have it's own satellite assemblies as well as transitively imported satellite assemblies. + var resxfileInProject = Path.Combine(blazorwasmProjectDirectory, "Resources.ja.resx.txt"); + File.Move(resxfileInProject, Path.Combine(blazorwasmProjectDirectory, "Resource.ja.resx")); - var standaloneProjFile = Path.Combine(standaloneProjectDirectory, "standalone.csproj"); - var existing = File.ReadAllText(standaloneProjFile); + var blazorwasmProjFile = Path.Combine(blazorwasmProjectDirectory, "blazorwasm.csproj"); + var existing = File.ReadAllText(blazorwasmProjFile); var updatedContent = @" - - $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies - - - -"; + + $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies + + + + "; var updated = existing.Replace("", updatedContent); - File.WriteAllText(standaloneProjFile, updated); + File.WriteAllText(blazorwasmProjFile, updated); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build", $"/restore"); @@ -517,11 +566,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "ja", "standalone.resources.dll"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.resources.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "ja", "blazorwasm.resources.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "fr", "Microsoft.CodeAnalysis.resources.dll"); var bootJson = Path.Combine(project.DirectoryPath, blazorPublishDirectory, "_framework", "blazor.boot.json"); - var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); var satelliteResources = bootJsonFile.resources.satelliteResources; Assert.Contains("es-ES", satelliteResources.Keys); @@ -529,7 +578,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.Contains("fr", satelliteResources.Keys); Assert.Contains("fr/Microsoft.CodeAnalysis.CSharp.resources.dll", satelliteResources["fr"].Keys); Assert.Contains("ja", satelliteResources.Keys); - Assert.Contains("ja/standalone.resources.dll", satelliteResources["ja"].Keys); + Assert.Contains("ja/blazorwasm.resources.dll", satelliteResources["ja"].Keys); VerifyServiceWorkerFiles(result, blazorPublishDirectory, serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), @@ -537,9 +586,64 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build assetsManifestPath: "custom-service-worker-assets.js"); } - private static void AddSiblingProjectFileContent(ProjectDirectory project, string content) + [Fact] + public async Task Publish_HostedApp_WithRid_Works() { - var path = Path.Combine(project.SolutionPath, "standalone", "standalone.csproj"); + // Arrange + using var project = ProjectDirectory.Create("blazorhosted-rid", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); + project.RuntimeIdentifier = "linux-x64"; + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + + Assert.BuildPassed(result); + + var publishDirectory = project.PublishOutputDirectory; + // Make sure the main project exists + Assert.FileExists(result, publishDirectory, "blazorhosted-rid.dll"); + Assert.FileExists(result, publishDirectory, "libhostfxr.so"); // Verify that we're doing a self-contained deployment + + Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll"); + Assert.FileExists(result, publishDirectory, "blazorwasm.dll"); + + var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. + + // Verify project references appear as static web assets + Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll"); + // Also verify project references to the server project appear in the publish output + Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll"); + + // Verify static assets are in the publish directory + Assert.FileExists(result, blazorPublishDirectory, "index.html"); + + // Verify static web assets from referenced projects are copied. + Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js"); + Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css"); + + // Verify web.config + Assert.FileExists(result, publishDirectory, "web.config"); + + VerifyBootManifestHashes(result, blazorPublishDirectory); + + // Verify compression works + Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br"); + Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); + + VerifyServiceWorkerFiles(result, blazorPublishDirectory, + serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), + serviceWorkerContent: "// This is the production service worker", + assetsManifestPath: "custom-service-worker-assets.js"); + } + + private static void AddWasmProjectContent(ProjectDirectory project, string content) + { + var path = Path.Combine(project.SolutionPath, "blazorwasm", "blazorwasm.csproj"); var existing = File.ReadAllText(path); var updated = existing.Replace("", content); File.WriteAllText(path, updated); @@ -555,102 +659,43 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build { var bootManifestResolvedPath = Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); var bootManifestJson = File.ReadAllText(bootManifestResolvedPath); - var bootManifest = JsonSerializer.Deserialize(bootManifestJson); + var bootManifest = JsonSerializer.Deserialize(bootManifestJson); - VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.assembly, r => $"_framework/_bin/{r}"); - VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.runtime, r => $"_framework/wasm/{r}"); + VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.assembly); + VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.runtime); if (bootManifest.resources.pdb != null) { - VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.pdb, r => $"_framework/_bin/{r}"); + VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.pdb); } if (bootManifest.resources.satelliteResources != null) { foreach (var resourcesForCulture in bootManifest.resources.satelliteResources.Values) { - VerifyBootManifestHashes(result, blazorPublishDirectory, resourcesForCulture, r => $"_framework/_bin/{r}"); + VerifyBootManifestHashes(result, blazorPublishDirectory, resourcesForCulture); } } - } - private static void VerifyBootManifestHashes(MSBuildResult result, string blazorPublishDirectory, ResourceHashesByNameDictionary resources, Func relativePathFunc) - { - foreach (var (name, hash) in resources) + static void VerifyBootManifestHashes(MSBuildResult result, string blazorPublishDirectory, ResourceHashesByNameDictionary resources) { - var relativePath = Path.Combine(blazorPublishDirectory, relativePathFunc(name)); - Assert.FileHashEquals(result, relativePath, ParseWebFormattedHash(hash)); - } - } - - private static void VerifyServiceWorkerFiles(MSBuildResult result, string blazorPublishDirectory, string serviceWorkerPath, string serviceWorkerContent, string assetsManifestPath) - { - // Check the expected files are there - var serviceWorkerResolvedPath = Assert.FileExists(result, blazorPublishDirectory, serviceWorkerPath); - var assetsManifestResolvedPath = Assert.FileExists(result, blazorPublishDirectory, assetsManifestPath); - - // Check the service worker contains the expected content (which comes from the PublishedContent file) - Assert.FileContains(result, serviceWorkerResolvedPath, serviceWorkerContent); - - // Check the assets manifest version was added to the published service worker - var assetsManifest = ReadServiceWorkerAssetsManifest(assetsManifestResolvedPath); - Assert.FileContains(result, serviceWorkerResolvedPath, $"/* Manifest version: {assetsManifest.version} */"); - - // Check the assets manifest contains correct entries for all static content we're publishing - var resolvedPublishDirectory = Path.Combine(result.Project.DirectoryPath, blazorPublishDirectory); - var publishedStaticFiles = Directory.GetFiles(resolvedPublishDirectory, "*", new EnumerationOptions { RecurseSubdirectories = true }); - var assetsManifestHashesByUrl = (IReadOnlyDictionary)assetsManifest.assets.ToDictionary(x => x.url, x => x.hash); - foreach (var publishedFilePath in publishedStaticFiles) - { - var publishedFileRelativePath = Path.GetRelativePath(resolvedPublishDirectory, publishedFilePath); - - // We don't list compressed files in the SWAM, as these are transparent to the client, - // nor do we list the service worker itself or its assets manifest, as these don't need to be fetched in the same way - if (IsCompressedFile(publishedFileRelativePath) - || string.Equals(publishedFileRelativePath, serviceWorkerPath, StringComparison.Ordinal) - || string.Equals(publishedFileRelativePath, assetsManifestPath, StringComparison.Ordinal)) + foreach (var (name, hash) in resources) { - continue; + var relativePath = Path.Combine(blazorPublishDirectory, "_framework", name); + Assert.FileHashEquals(result, relativePath, ParseWebFormattedHash(hash)); } - - // Verify hash - var publishedFileUrl = publishedFileRelativePath.Replace('\\', '/'); - var expectedHash = ParseWebFormattedHash(assetsManifestHashesByUrl[publishedFileUrl]); - Assert.Contains(publishedFileUrl, assetsManifestHashesByUrl); - Assert.FileHashEquals(result, publishedFilePath, expectedHash); } - } - private static string ParseWebFormattedHash(string webFormattedHash) - { - Assert.StartsWith("sha256-", webFormattedHash); - return webFormattedHash.Substring(7); - } - - private static bool IsCompressedFile(string path) - { - switch (Path.GetExtension(path)) + static string ParseWebFormattedHash(string webFormattedHash) { - case ".br": - case ".gz": - return true; - default: - return false; + Assert.StartsWith("sha256-", webFormattedHash); + return webFormattedHash.Substring(7); } } - private static GenerateServiceWorkerAssetsManifest.AssetsManifestFile ReadServiceWorkerAssetsManifest(string assetsManifestResolvedPath) + private static BootJsonData ReadBootJsonData(MSBuildResult result, string path) { - var jsContents = File.ReadAllText(assetsManifestResolvedPath); - var jsonStart = jsContents.IndexOf("{"); - var jsonLength = jsContents.LastIndexOf("}") - jsonStart + 1; - var json = jsContents.Substring(jsonStart, jsonLength); - return JsonSerializer.Deserialize(json); - } - - private static GenerateBlazorBootJson.BootJsonData ReadBootJsonData(MSBuildResult result, string path) - { - return JsonSerializer.Deserialize( + return JsonSerializer.Deserialize( File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs new file mode 100644 index 0000000000..9bf091533b --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs @@ -0,0 +1,211 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Xunit; +using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert; + + +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests +{ + public class WasmPwaManifestTests + { + private static readonly string DotNetJsFileName = $"dotnet.{BuildVariables.MicrosoftNETCoreAppRuntimeVersion}.js"; + + [Fact] + public async Task Build_ServiceWorkerAssetsManifest_Works() + { + // Arrange + var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" }; + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:ServiceWorkerAssetsManifest=service-worker-assets.js"); + + Assert.BuildPassed(result); + + var buildOutputDirectory = project.BuildOutputDirectory; + + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm"); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName); + Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll"); + + var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm.StaticWebAssets.xml"); + Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot")); + + var serviceWorkerAssetsManifest = Assert.FileExists(result, buildOutputDirectory, "wwwroot", "service-worker-assets.js"); + // Trim prefix 'self.assetsManifest = ' and suffix ';' + var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1]; + + var manifestContentsJson = JsonDocument.Parse(manifestContents); + Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets)); + Assert.Equal(JsonValueKind.Array, assets.ValueKind); + + var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray(); + Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e))); + + VerifyServiceWorkerFiles(result, + Path.Combine(buildOutputDirectory, "wwwroot"), + serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), + serviceWorkerContent: "// This is the development service worker", + assetsManifestPath: "service-worker-assets.js"); + } + + [Fact] + public async Task Build_HostedAppWithServiceWorker_Works() + { + // Arrange + var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" }; + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + Assert.BuildPassed(result); + + var wasmProject = project.GetSibling("blazorwasm"); + var buildOutputDirectory = Path.Combine(wasmProject.DirectoryPath, wasmProject.BuildOutputDirectory); + + var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm.StaticWebAssets.xml"); + Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot")); + + var serviceWorkerAssetsManifest = Assert.FileExists(result, buildOutputDirectory, "wwwroot", "custom-service-worker-assets.js"); + // Trim prefix 'self.assetsManifest = ' and suffix ';' + var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1]; + + var manifestContentsJson = JsonDocument.Parse(manifestContents); + Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets)); + Assert.Equal(JsonValueKind.Array, assets.ValueKind); + + var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray(); + Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e))); + } + + [Fact] + public async Task PublishWithPWA_ProducesAssets() + { + // Arrange + var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" }; + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "Publish"); + + Assert.BuildPassed(result); + + var publishOutputDirectory = project.PublishOutputDirectory; + + var serviceWorkerAssetsManifest = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "custom-service-worker-assets.js"); + // Trim prefix 'self.assetsManifest = ' and suffix ';' + var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1]; + + var manifestContentsJson = JsonDocument.Parse(manifestContents); + Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets)); + Assert.Equal(JsonValueKind.Array, assets.ValueKind); + + var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray(); + Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e))); + + var serviceWorkerFile = Path.Combine(publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js"); + Assert.FileContainsLine(result, serviceWorkerFile, "// This is the production service worker"); + } + + [Fact] + public async Task PublishHostedWithPWA_ProducesAssets() + { + // Arrange + var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" }; + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "Publish"); + + Assert.BuildPassed(result); + + var publishOutputDirectory = project.PublishOutputDirectory; + + var serviceWorkerAssetsManifest = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "custom-service-worker-assets.js"); + // Trim prefix 'self.assetsManifest = ' and suffix ';' + var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1]; + + var manifestContentsJson = JsonDocument.Parse(manifestContents); + Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets)); + Assert.Equal(JsonValueKind.Array, assets.ValueKind); + + var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray(); + Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e))); + + var serviceWorkerFile = Path.Combine(publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js"); + Assert.FileContainsLine(result, serviceWorkerFile, "// This is the production service worker"); + } + + [Fact] + public async Task Publish_UpdatesServiceWorkerVersionHash_WhenSourcesChange() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); + + Assert.BuildPassed(result); + + var publishOutputDirectory = project.PublishOutputDirectory; + + var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js"); + var version = File.ReadAllLines(serviceWorkerFile).Last(); + var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/"); + Assert.True(match.Success); + Assert.Equal(2, match.Groups.Count); + Assert.NotNull(match.Groups[1].Value); + var capture = match.Groups[1].Value; + + // Act + var cssFile = Path.Combine(project.DirectoryPath, "LinkToWebRoot", "css", "app.css"); + File.WriteAllText(cssFile, ".updated { }"); + + // Assert + result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); + + Assert.BuildPassed(result); + + var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last(); + var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/"); + Assert.True(updatedMatch.Success); + Assert.Equal(2, updatedMatch.Groups.Count); + Assert.NotNull(updatedMatch.Groups[1].Value); + var updatedCapture = updatedMatch.Groups[1].Value; + + Assert.NotEqual(capture, updatedCapture); + } + + [Fact] + public async Task Publish_DeterministicAcrossBuilds_WhenNoSourcesChange() + { + // Arrange + using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); + + Assert.BuildPassed(result); + + var publishOutputDirectory = project.PublishOutputDirectory; + + var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js"); + var version = File.ReadAllLines(serviceWorkerFile).Last(); + var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/"); + Assert.True(match.Success, $"No match found for manifest version regex in line: {version}"); + Assert.Equal(2, match.Groups.Count); + Assert.NotNull(match.Groups[1].Value); + var capture = match.Groups[1].Value; + + // Act && Assert + result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); + + Assert.BuildPassed(result); + + var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last(); + var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/"); + Assert.True(updatedMatch.Success); + Assert.Equal(2, updatedMatch.Groups.Count); + Assert.NotNull(updatedMatch.Groups[1].Value); + var updatedCapture = updatedMatch.Groups[1].Value; + + Assert.Equal(capture, updatedCapture); + } + } +} diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/AssetsManifestFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/AssetsManifestFile.cs new file mode 100644 index 0000000000..34dde41812 --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/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.AspNetCore.Razor.Tasks +{ +#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/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorReadSatelliteAssemblyFile.cs similarity index 95% rename from src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs rename to src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorReadSatelliteAssemblyFile.cs index 18a3fc8214..f2f95dbf26 100644 --- a/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorReadSatelliteAssemblyFile.cs @@ -6,7 +6,7 @@ using System.Xml.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -namespace Microsoft.AspNetCore.Components.WebAssembly.Build +namespace Microsoft.AspNetCore.Razor.Tasks { public class BlazorReadSatelliteAssemblyFile : Task { diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorWriteSatelliteAssemblyFile.cs similarity index 96% rename from src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs rename to src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorWriteSatelliteAssemblyFile.cs index e1e6a8150e..da07cf6d00 100644 --- a/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorWriteSatelliteAssemblyFile.cs @@ -7,7 +7,7 @@ using System.Xml.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -namespace Microsoft.AspNetCore.Components.WebAssembly.Build +namespace Microsoft.AspNetCore.Razor.Tasks { public class BlazorWriteSatelliteAssemblyFile : Task { diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/BootJsonData.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/BootJsonData.cs new file mode 100644 index 0000000000..0952070c8d --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/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.AspNetCore.Razor.Tasks +{ +#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 dynamically during runtime + /// + [DataMember(EmitDefaultValue = false)] + public ResourceHashesByNameDictionary dynamicAssembly { get; set; } + } +#pragma warning restore IDE1006 // Naming Styles +} diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/BrotliCompress.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/BrotliCompress.cs new file mode 100644 index 0000000000..9019f0520f --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/BrotliCompress.cs @@ -0,0 +1,99 @@ +// 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.Security.Cryptography; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.AspNetCore.Razor.Tasks +{ + public class BrotliCompress : DotNetToolTask + { + private static readonly char[] InvalidPathChars = Path.GetInvalidFileNameChars(); + + [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; } + + internal override string Command => "brotli"; + + protected override string GenerateResponseFileCommands() + { + var builder = new StringBuilder(); + + builder.AppendLine(Command); + + 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(relativePath)); + + var outputItem = new TaskItem(outputRelativePath); + input.CopyMetadataTo(outputItem); + // Relative path in the publish dir + outputItem.SetMetadata("RelativePath", relativePath + ".br"); + CompressedFiles[i] = outputItem; + + var outputFullPath = Path.GetFullPath(outputRelativePath); + + 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(); + } + + private static string CalculateTargetPath(string relativePath) + { + // 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(".br"); + return builder.ToString(); + } + } +} diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs similarity index 50% rename from src/Components/WebAssembly/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs rename to src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs index 45359bdb8a..57fa43a979 100644 --- a/src/Components/WebAssembly/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs @@ -3,42 +3,53 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using System.Xml; using System.Xml.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -namespace Microsoft.AspNetCore.Components.WebAssembly.Build +namespace Microsoft.AspNetCore.Razor.Tasks { // Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/CreateRootDescriptorFile.cs - public class BlazorCreateRootDescriptorFile : Task + public class CreateBlazorTrimmerRootDescriptorFile : Task { [Required] - public ITaskItem[] AssemblyNames { get; set; } + public ITaskItem[] Assemblies { get; set; } [Required] - public ITaskItem RootDescriptorFilePath { get; set; } + public ITaskItem TrimmerFile { get; set; } public override bool Execute() { - using var fileStream = File.Create(RootDescriptorFilePath.ItemSpec); - var assemblyNames = AssemblyNames.Select(a => a.ItemSpec); + using var fileStream = File.Create(TrimmerFile.ItemSpec); - WriteRootDescriptor(fileStream, assemblyNames); + WriteRootDescriptor(fileStream); return true; } - internal static void WriteRootDescriptor(Stream stream, IEnumerable assemblyNames) + internal void WriteRootDescriptor(Stream stream) { var roots = new XElement("linker"); - foreach (var assemblyName in assemblyNames) + foreach (var assembly in Assemblies) { + var assemblyName = assembly.GetMetadata("FileName") + assembly.GetMetadata("Extension"); + 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", - new XAttribute("fullname", "*"), - new XAttribute("required", "true")))); + new XElement("type", attributes))); } var xmlWriterSettings = new XmlWriterSettings diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateBlazorWebAssemblyBootJson.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateBlazorWebAssemblyBootJson.cs new file mode 100644 index 0000000000..4d2c786443 --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/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.AspNetCore.Razor.Tasks +{ + 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.dynamicAssembly ??= new ResourceHashesByNameDictionary(); + resourceList = resourceData.dynamicAssembly; + } + 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/Razor/Microsoft.NET.Sdk.Razor/src/GenerateServiceWorkerAssetsManifest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateServiceWorkerAssetsManifest.cs new file mode 100644 index 0000000000..8e5f0d39e7 --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/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.AspNetCore.Razor.Tasks +{ + 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/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj b/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj index efe499aaca..42cb964335 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj @@ -34,12 +34,10 @@ - diff --git a/src/Components/WebAssembly/Build/src/targets/Standalone.Web.config b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/BlazorWasm.web.config similarity index 100% rename from src/Components/WebAssembly/Build/src/targets/Standalone.Web.config rename to src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/BlazorWasm.web.config diff --git a/src/Components/WebAssembly/Build/src/targets/BuiltInBclLinkerDescriptor.xml b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/LinkerWorkaround.xml similarity index 84% rename from src/Components/WebAssembly/Build/src/targets/BuiltInBclLinkerDescriptor.xml rename to src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/LinkerWorkaround.xml index 6fb65f5a14..4461d2fc12 100644 --- a/src/Components/WebAssembly/Build/src/targets/BuiltInBclLinkerDescriptor.xml +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/LinkerWorkaround.xml @@ -4,12 +4,6 @@ by the IL linker even if they are not referenced by user code. The file format is described at https://github.com/mono/linker/blob/master/src/linker/README.md#syntax-of-xml-descriptor --> - - - - - @@ -19,6 +13,8 @@ + + <_RazorGenerateInputsHash> <_RazorGenerateInputsHashFile>$(IntermediateOutputPath)$(MSBuildProjectName).RazorCoreGenerate.cache - - <_RazorToolAssembly Condition="'$(_RazorToolAssembly)'==''">$(RazorSdkDirectoryRoot)tools\netcoreapp3.0\rzc.dll + + + + + + + <_ServiceWorkerAssetsManifestIntermediateOutputPath>$(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="%(ServiceWorker.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/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets new file mode 100644 index 0000000000..fe977ecdc0 --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets @@ -0,0 +1,490 @@ + + + + + + + + + + + true + true + true + + / + true + + + false + false + false + false + false + exe + + + <_BlazorOutputPath>wwwroot\_framework\ + <_BlazorSatelliteAssemblyCacheFile>$(IntermediateOutputPath)blazor.satelliteasm.props + + + $(OutputPath)$(PublishDirName)\ + + + + + + + + + + $(IntermediateOutputPath)linked\ + $(IntermediateLinkDir)\ + + <_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore + + + + + + + + <_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'" /> + + + + + + + + + + + + + + + <_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)' == ''" /> + + + + + + <_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 + + + + <_ExternalStaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" SourceType="Generated" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_BlazorApplicationTrimmerDescriptorFile>$(IntermediateOutputPath)default.trimmerdescriptor.xml + <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml + + + + <_BlazorTypeGranularAssembly + Include="@(ResolvedFileToPublish)" + Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.'))"> + false + all + + + <_BlazorApplicationAssembly Include="@(IntermediateAssembly)"> + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_BlazorPublishBootResource + Include="@(ResolvedFileToPublish)" + Condition="$([System.String]::Copy('%(RelativePath)').Replace('\','/').StartsWith('wwwroot/_framework')) AND '%(Extension)' != '.a'" /> + + + + + + + + + + + + + + + + + + <_CompressedFileOutputPath>$(IntermediateOutputPath)brotli\ + <_BlazorWebAssemblyBrotliIncremental>true + + + + <_BrotliFileToCompress + Include="@(ResolvedFileToPublish)" + Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))" /> + + + + + + + + + + + + + + + + + + + + + <_PublishingBlazorWasmProject>true + + + + + + + + + + + + + + %(RelativePath) + PreserveNewest + + + + + + + + + + + diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets index 9a66b1b926..71721b675d 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets @@ -269,7 +269,7 @@ Copyright (c) .NET Foundation. All rights reserved. + KeepMetadata="ContentRoot;BasePath;RelativePath;SourceId;SourceType;CopyToPublishDirectory" /> @@ -291,7 +291,7 @@ Copyright (c) .NET Foundation. All rights reserved. - + @@ -513,7 +513,7 @@ Copyright (c) .NET Foundation. All rights reserved. --> <_ExternalPublishStaticWebAsset Include="%(StaticWebAsset.FullPath)" - Condition="'%(SourceType)' != ''"> + Condition="'%(SourceType)' != '' AND '%(StaticWebAsset.CopyToPublishDirectory)' != 'Never'"> PreserveNewest $([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)','$([MSBuild]::NormalizePath('wwwroot\%(BasePath)\%(RelativePath)'))')) diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.props b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.props index 6fad4d5180..7e49bc4277 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.props +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.props @@ -14,4 +14,5 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.props $(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.targets - + + \ No newline at end of file diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props index b7d17b206b..19e380c3a4 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props @@ -18,6 +18,8 @@ Copyright (c) .NET Foundation. All rights reserved. Default properties for common Razor SDK behavior. --> + true + <_TargetFrameworkVersionWithoutV>$(TargetFrameworkVersion.TrimStart('vV')) <_TargetingNETCoreApp30OrLater Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' >= '3.0'">true + <_TargetingNET50OrLater Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' >= '5.0'">true OutOfProcess + + + <_BlazorBrotliCompressionLevel>NoCompression @@ -37,7 +41,6 @@ <_MvcAssemblyName Include="Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib" /> - + + + diff --git a/src/Razor/test/testassets/SimpleMvc21/SimpleMvc21.csproj b/src/Razor/test/testassets/SimpleMvc21/SimpleMvc21.csproj index 2f53950aa6..292a0a83df 100644 --- a/src/Razor/test/testassets/SimpleMvc21/SimpleMvc21.csproj +++ b/src/Razor/test/testassets/SimpleMvc21/SimpleMvc21.csproj @@ -7,6 +7,7 @@ netcoreapp2.1 + $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\ diff --git a/src/Razor/test/testassets/blazor.webassembly.js b/src/Razor/test/testassets/blazor.webassembly.js new file mode 100644 index 0000000000..84362ca046 --- /dev/null +++ b/src/Razor/test/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/Razor/test/testassets/blazorhosted-rid/Program.cs new file mode 100644 index 0000000000..cb0046ec11 --- /dev/null +++ b/src/Razor/test/testassets/blazorhosted-rid/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace blazorhosted.Server +{ + public class Program + { + public static void Main(string[] args) + { + Console.WriteLine(typeof(string)); + } + } +} diff --git a/src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj b/src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj new file mode 100644 index 0000000000..1e14923fb3 --- /dev/null +++ b/src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj @@ -0,0 +1,22 @@ + + + + $(DefaultNetCoreTargetFramework) + $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\ + + linux-x64 + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/Build/testassets/blazorhosted/Program.cs b/src/Razor/test/testassets/blazorhosted/Program.cs similarity index 100% rename from src/Components/WebAssembly/Build/testassets/blazorhosted/Program.cs rename to src/Razor/test/testassets/blazorhosted/Program.cs diff --git a/src/Razor/test/testassets/blazorhosted/blazorhosted.csproj b/src/Razor/test/testassets/blazorhosted/blazorhosted.csproj new file mode 100644 index 0000000000..28c8a47ce5 --- /dev/null +++ b/src/Razor/test/testassets/blazorhosted/blazorhosted.csproj @@ -0,0 +1,12 @@ + + + + $(DefaultNetCoreTargetFramework) + $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\ + + + + + + + diff --git a/src/Components/WebAssembly/Build/testassets/standalone/App.razor b/src/Razor/test/testassets/blazorwasm/App.razor similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/App.razor rename to src/Razor/test/testassets/blazorwasm/App.razor diff --git a/src/Components/WebAssembly/Build/testassets/standalone/LinkToWebRoot/css/app.css b/src/Razor/test/testassets/blazorwasm/LinkToWebRoot/css/app.css similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/LinkToWebRoot/css/app.css rename to src/Razor/test/testassets/blazorwasm/LinkToWebRoot/css/app.css diff --git a/src/Components/WebAssembly/Build/testassets/standalone/Pages/Index.razor b/src/Razor/test/testassets/blazorwasm/Pages/Index.razor similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/Pages/Index.razor rename to src/Razor/test/testassets/blazorwasm/Pages/Index.razor diff --git a/src/Components/WebAssembly/Build/testassets/standalone/Program.cs b/src/Razor/test/testassets/blazorwasm/Program.cs similarity index 83% rename from src/Components/WebAssembly/Build/testassets/standalone/Program.cs rename to src/Razor/test/testassets/blazorwasm/Program.cs index 7d2cb4eeea..4b20dea9de 100644 --- a/src/Components/WebAssembly/Build/testassets/standalone/Program.cs +++ b/src/Razor/test/testassets/blazorwasm/Program.cs @@ -6,6 +6,7 @@ namespace standalone { public static void Main(string[] args) { + GC.KeepAlive(typeof(System.Text.Json.JsonSerializer)); GC.KeepAlive(typeof(RazorClassLibrary.Class1)); #if REFERENCE_classlibrarywithsatelliteassemblies GC.KeepAlive(typeof(classlibrarywithsatelliteassemblies.Class1)); diff --git a/src/Components/WebAssembly/Build/testassets/standalone/Resources.ja.resx.txt b/src/Razor/test/testassets/blazorwasm/Resources.ja.resx.txt similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/Resources.ja.resx.txt rename to src/Razor/test/testassets/blazorwasm/Resources.ja.resx.txt diff --git a/src/Components/WebAssembly/Build/testassets/standalone/_Imports.razor b/src/Razor/test/testassets/blazorwasm/_Imports.razor similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/_Imports.razor rename to src/Razor/test/testassets/blazorwasm/_Imports.razor diff --git a/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj b/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj new file mode 100644 index 0000000000..724a4365b5 --- /dev/null +++ b/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj @@ -0,0 +1,45 @@ + + + net5.0 + true + browser-wasm + false + $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\ + custom-service-worker-assets.js + + + + + + + false + + + + + $(RepoRoot)artifacts\bin\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib\$(Configuration)\netstandard2.0\ + + + + + + + + + + + + wwwroot\ + + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/Fake-License.txt b/src/Razor/test/testassets/blazorwasm/wwwroot/Fake-License.txt similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/Fake-License.txt rename to src/Razor/test/testassets/blazorwasm/wwwroot/Fake-License.txt diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/css/app.css b/src/Razor/test/testassets/blazorwasm/wwwroot/css/app.css similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/css/app.css rename to src/Razor/test/testassets/blazorwasm/wwwroot/css/app.css diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/index.html b/src/Razor/test/testassets/blazorwasm/wwwroot/index.html similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/index.html rename to src/Razor/test/testassets/blazorwasm/wwwroot/index.html diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/serviceworkers/my-prod-service-worker.js b/src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/serviceworkers/my-prod-service-worker.js rename to src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/serviceworkers/my-service-worker.js b/src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js similarity index 100% rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/serviceworkers/my-service-worker.js rename to src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js diff --git a/src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/Class1.cs b/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Class1.cs similarity index 100% rename from src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/Class1.cs rename to src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Class1.cs diff --git a/src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx b/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx similarity index 100% rename from src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx rename to src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx diff --git a/src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj b/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj similarity index 77% rename from src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj rename to src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj index 7081842748..f1a186fb94 100644 --- a/src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj +++ b/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj @@ -3,6 +3,7 @@ netstandard2.1 3.0 + $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\ diff --git a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/Class1.cs b/src/Razor/test/testassets/razorclasslibrary/Class1.cs similarity index 100% rename from src/Components/WebAssembly/Build/testassets/razorclasslibrary/Class1.cs rename to src/Razor/test/testassets/razorclasslibrary/Class1.cs diff --git a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/RazorClassLibrary.csproj b/src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj similarity index 63% rename from src/Components/WebAssembly/Build/testassets/razorclasslibrary/RazorClassLibrary.csproj rename to src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj index 94e866815d..9480a0542e 100644 --- a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/RazorClassLibrary.csproj +++ b/src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj @@ -3,6 +3,7 @@ netstandard2.1 3.0 + $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\ diff --git a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/wwwroot/styles.css b/src/Razor/test/testassets/razorclasslibrary/wwwroot/styles.css similarity index 100% rename from src/Components/WebAssembly/Build/testassets/razorclasslibrary/wwwroot/styles.css rename to src/Razor/test/testassets/razorclasslibrary/wwwroot/styles.css diff --git a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js b/src/Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js similarity index 100% rename from src/Components/WebAssembly/Build/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js rename to src/Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js