diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs b/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs new file mode 100644 index 0000000000..18a3fc8214 --- /dev/null +++ b/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.AspNetCore.Components.WebAssembly.Build +{ + public class BlazorReadSatelliteAssemblyFile : Task + { + [Output] + public ITaskItem[] SatelliteAssembly { get; set; } + + [Required] + public ITaskItem ReadFile { get; set; } + + public override bool Execute() + { + var document = XDocument.Load(ReadFile.ItemSpec); + SatelliteAssembly = document.Root + .Elements() + .Select(e => + { + // + + var taskItem = new TaskItem(e.Attribute("Name").Value); + taskItem.SetMetadata("Culture", e.Attribute("Culture").Value); + taskItem.SetMetadata("DestinationSubDirectory", e.Attribute("DestinationSubDirectory").Value); + + return taskItem; + }).ToArray(); + + return true; + } + } +} diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs b/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs new file mode 100644 index 0000000000..e1e6a8150e --- /dev/null +++ b/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs @@ -0,0 +1,53 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.AspNetCore.Components.WebAssembly.Build +{ + public class BlazorWriteSatelliteAssemblyFile : Task + { + [Required] + public ITaskItem[] SatelliteAssembly { get; set; } + + [Required] + public ITaskItem WriteFile { get; set; } + + public override bool Execute() + { + using var fileStream = File.Create(WriteFile.ItemSpec); + WriteSatelliteAssemblyFile(fileStream); + return true; + } + + internal void WriteSatelliteAssemblyFile(Stream stream) + { + var root = new XElement("SatelliteAssembly"); + + foreach (var item in SatelliteAssembly) + { + // + + root.Add(new XElement("Assembly", + new XAttribute("Name", item.ItemSpec), + new XAttribute("Culture", item.GetMetadata("Culture")), + new XAttribute("DestinationSubDirectory", item.GetMetadata("DestinationSubDirectory")))); + } + + var xmlWriterSettings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true + }; + + using var writer = XmlWriter.Create(stream, xmlWriterSettings); + var xDocument = new XDocument(root); + + xDocument.Save(writer); + } + } +} diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets index 8cb135f996..7d6eb810b4 100644 --- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets @@ -75,6 +75,9 @@ + + + + + + <_BlazorSatelliteAssemblyStashFile>$(_BlazorIntermediateOutputPath)blazor.satelliteasm.props + + + + + + + + + + + + + + + + + <_BlazorOutputWithTargetPath Include="@(_BlazorReadSatelliteAssembly)"> + satellite + %(_BlazorReadSatelliteAssembly.DestinationSubDirectory)%(FileName)%(Extension) + $(_BlazorRuntimeBinOutputPath)%(_BlazorReadSatelliteAssembly.DestinationSubDirectory)%(FileName)%(Extension) + ", updatedContent); + File.WriteAllText(standaloneProjFile, updated); + + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build", $"/restore"); + + Assert.BuildPassed(result); + + result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/p:BuildProjectReferences=false"); + + var publishDirectory = project.PublishOutputDirectory; + // Make sure the main project exists + Assert.FileExists(result, publishDirectory, "blazorhosted.dll"); + + 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"); + + var bootJson = Path.Combine(project.DirectoryPath, blazorPublishDirectory, "_framework", "blazor.boot.json"); + var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + var satelliteResources = bootJsonFile.resources.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); + + 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 AddSiblingProjectFileContent(ProjectDirectory project, string content) { var path = Path.Combine(project.SolutionPath, "standalone", "standalone.csproj"); @@ -543,5 +645,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build var json = jsContents.Substring(jsonStart, jsonLength); return JsonSerializer.Deserialize(json); } + + private static GenerateBlazorBootJson.BootJsonData ReadBootJsonData(MSBuildResult result, string path) + { + return JsonSerializer.Deserialize( + File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)), + new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } } }