diff --git a/src/Microsoft.DocAsCode.App/Helpers/DocumentBuilderWrapper.cs b/src/Microsoft.DocAsCode.App/Helpers/DocumentBuilderWrapper.cs index bf1cc56fde0..eeded8eb25a 100644 --- a/src/Microsoft.DocAsCode.App/Helpers/DocumentBuilderWrapper.cs +++ b/src/Microsoft.DocAsCode.App/Helpers/DocumentBuilderWrapper.cs @@ -135,14 +135,10 @@ private static List ConfigToParameter(BuildJsonConfig c TagParameters = config.TagParameters, ConfigureMarkdig = options.ConfigureMarkdig, }; - if (config.GlobalMetadata != null) - { - parameters.Metadata = config.GlobalMetadata.ToImmutableDictionary(); - } - if (config.FileMetadata != null) - { - parameters.FileMetadata = ConvertToFileMetadataItem(baseDirectory, config.FileMetadata); - } + + parameters.Metadata = GetGlobalMetadata(config); + parameters.FileMetadata = GetFileMetadata(baseDirectory, config); + if (config.PostProcessors != null) { parameters.PostProcessors = config.PostProcessors.ToImmutableArray(); @@ -254,6 +250,66 @@ private static List ConfigToParameter(BuildJsonConfig c } } + private static ImmutableDictionary GetGlobalMetadata(BuildJsonConfig config) + { + var result = new Dictionary(); + + if (config.GlobalMetadata != null) + { + foreach (var (key, value) in config.GlobalMetadata) + { + result[key] = value; + } + } + + if (config.GlobalMetadataFilePaths != null) + { + foreach (var path in config.GlobalMetadataFilePaths) + { + foreach (var (key, value) in JsonUtility.Deserialize>(path)) + { + result[key] = value; + } + } + } + + return result.ToImmutableDictionary(); + } + + private static FileMetadata GetFileMetadata(string baseDirectory, BuildJsonConfig config) + { + var result = new Dictionary>(); + + if (config.FileMetadata != null) + { + foreach (var (key, value) in config.FileMetadata) + { + var list = result.TryGetValue(key, out var items) ? items : result[key] = new(); + foreach (var pair in value.Items) + { + list.Add(new FileMetadataItem(pair.Glob, key, pair.Value)); + } + } + } + + if (config.FileMetadataFilePaths != null) + { + foreach (var path in config.FileMetadataFilePaths) + { + foreach (var (key, value) in JsonUtility.Deserialize>(path)) + { + var list = result.TryGetValue(key, out var items) ? items : result[key] = new(); + foreach (var pair in value.Items) + { + list.Add(new FileMetadataItem(pair.Glob, key, pair.Value)); + } + } + } + } + + return new FileMetadata(baseDirectory, result.ToDictionary(p => p.Key, p => p.Value.ToImmutableArray())); + } + /// /// Group FileMappings to a dictionary using VersionName as the key. /// As default version has no VersionName, using empty string as the key. @@ -303,36 +359,6 @@ private static void AddFileMappingTypeGroup( } } - private static FileMetadata ConvertToFileMetadataItem(string baseDirectory, Dictionary fileMetadata) - { - var result = new Dictionary>(); - foreach (var item in fileMetadata) - { - var list = new List(); - foreach (var pair in item.Value.Items) - { - list.Add(new FileMetadataItem(pair.Glob, item.Key, pair.Value)); - } - result.Add(item.Key, list.ToImmutableArray()); - } - - return new FileMetadata(baseDirectory, result); - } - - private static IEnumerable GetFilesFromFileMapping(FileMapping mapping) - { - if (mapping != null) - { - foreach (var file in mapping.Items) - { - foreach (string item in file.Files) - { - yield return Path.Combine(file.SourceFolder ?? Directory.GetCurrentDirectory(), item); - } - } - } - } - private static FileCollection GetFileCollectionFromFileMapping( string baseDirectory, FileMapping articles, diff --git a/src/docfx/Models/OptionMerger.cs b/src/docfx/Models/OptionMerger.cs deleted file mode 100644 index 504b4c7da58..00000000000 --- a/src/docfx/Models/OptionMerger.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace Microsoft.DocAsCode; - -internal static class OptionMerger -{ - public delegate T Merger(string key, MergeContext item, MergeContext overrideItem); - - public static Dictionary MergeDictionary(DictionaryMergeContext item, DictionaryMergeContext overrideItem, Merger merger) - { - if (merger == null) - { - throw new ArgumentNullException(nameof(merger)); - } - - Dictionary merged; - if (overrideItem?.Item == null) - { - merged = new Dictionary(); - } - else - { - merged = new Dictionary(overrideItem.Item); - } - if (item?.Item == null) - { - return merged; - } - else - { - foreach (var pair in item.Item) - { - if (merged.TryGetValue(pair.Key, out T value)) - { - merged[pair.Key] = merger(pair.Key, new MergeContext(item.Name, pair.Value), new MergeContext(overrideItem.Name, value)); - } - else - { - merged[pair.Key] = pair.Value; - } - } - } - return merged; - } -} - -internal sealed class DictionaryMergeContext -{ - public string Name { get; } - public Dictionary Item { get; } - - public DictionaryMergeContext(string name, Dictionary item) - { - Name = name; - Item = item; - } -} - -internal sealed class MergeContext -{ - public string Name { get; } - public T Item { get; } - - public MergeContext(string name, T item) - { - Name = name; - Item = item; - } -} diff --git a/test/docfx.Tests/DocsetTest.cs b/test/docfx.Tests/DocsetTest.cs index 4e1ca526c6c..b13e8d093b0 100644 --- a/test/docfx.Tests/DocsetTest.cs +++ b/test/docfx.Tests/DocsetTest.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Runtime.CompilerServices; - +using System.Text.Json; using Microsoft.DocAsCode.Tests.Common; using Xunit; @@ -41,14 +41,14 @@ public static async Task CustomLogo_Override_LogoFromTemplate() { ["docfx.json"] = """ - { - "build": { - "resource": [{ "files": [ "logo.svg" ] }], - "template": ["default"], - "dest": "_site" - } + { + "build": { + "resource": [{ "files": [ "logo.svg" ] }], + "template": ["default"], + "dest": "_site" } - """, + } + """, ["logo.svg"] = "my svg" }); @@ -62,18 +62,108 @@ public static async Task Load_Custom_Plugin_From_Template() { ["docfx.json"] = """ - { - "build": { - "content": [{ "files": [ "*.md" ] }], - "template": ["default", "../../Assets/template"], - "dest": "_site", - "postProcessors": ["CustomPostProcessor"] - } + { + "build": { + "content": [{ "files": [ "*.md" ] }], + "template": ["default", "../../Assets/template"], + "dest": "_site", + "postProcessors": ["CustomPostProcessor"] } - """, + } + """, ["index.md"] = "" }); Assert.Equal("customPostProcessor", outputs["customPostProcessor.txt"]()); } + + [Fact] + public static async Task Build_With_Global_Metadata_Files() + { + var outputs = await Build(new() + { + ["docfx.json"] = + """ + { + "build": { + "content": [{ "files": [ "*.md" ] }], + "dest": "_site", + "exportRawModel": true, + "globalMetadataFiles": ["projectMetadata1.json", "projectMetadata2.json"], + "globalMetadata": { + "meta1": "docfx.json", + "meta3": "docfx.json" + } + } + } + """, + ["projectMetadata1.json"] = + """ + { + "meta1": "projectMetadata1.json", + "meta2": "projectMetadata2.json" + } + """, + ["projectMetadata2.json"] = + """ + { + "meta2": "projectMetadata2.json" + } + """, + ["index.md"] = "" + }); + + var metadata = JsonDocument.Parse(outputs["index.raw.json"]()).RootElement; + Assert.Equal("projectMetadata1.json", metadata.GetProperty("meta1").GetString()); + Assert.Equal("projectMetadata2.json", metadata.GetProperty("meta2").GetString()); + Assert.Equal("docfx.json", metadata.GetProperty("meta3").GetString()); + } + + [Fact] + public static async Task Build_With_File_Metadata_Files() + { + var outputs = await Build(new() + { + ["docfx.json"] = + """ + { + "build": { + "content": [{ "files": [ "*.md" ] }], + "dest": "_site", + "exportRawModel": true, + "fileMetadataFiles": ["fileMetadata1.json", "fileMetadata2.json"], + "fileMetadata": { + "meta1": { + "a.md": "docfx.json" + } + } + } + } + """, + ["fileMetadata1.json"] = + """ + { + "meta1": { + "b.md": "fileMetadata1.json" + } + } + """, + ["fileMetadata2.json"] = + """ + { + "meta1": { + "a.md": "fileMetadata2.json", + "b.md": "fileMetadata2.json" + } + } + """, + ["a.md"] = "", + ["b.md"] = "" + }); + + var a = JsonDocument.Parse(outputs["a.raw.json"]()).RootElement; + var b = JsonDocument.Parse(outputs["b.raw.json"]()).RootElement; + Assert.Equal("fileMetadata1.json", a.GetProperty("meta1").GetString()); + Assert.Equal("fileMetadata2.json", b.GetProperty("meta1").GetString()); + } }