Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support load plugins from template #8812

Merged
merged 1 commit into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,14 @@ Goal and limitation

Preparation
-----------
1. Create a new C# class library project in `Visual Studio`, targets .NET Framework 4.7.2 or later.
1. Create a new C# class library targeting `net6.0` or later.

2. Add nuget packages:
* `System.Collections.Immutable` with version 1.3.1 or later (if not already included in your .NET Framework target version)
* `Microsoft.Composition` with version 1.0.31
2. Add NuGet package reference to `System.Composition`, `Microsoft.DocAsCode.Plugins` and `Microsoft.DocAsCode.Common`.

3. Add `Microsoft.DocAsCode.Plugins` and `Microsoft.DocAsCode.Common`
If building DocFX from source code then add a reference to the project,
otherwise add the nuget packages with the same version as DocFX.

4. Add framework assembly references:
`PresentationCore`, `PresentationFramework`, `WindowsBase`. (This step is optional in Visual Studio 2017 or above)

5. Add a project for converting rtf to html:
4. Add a project for converting rtf to html:
Clone project [MarkupConverter](https://github.com/mmanela/MarkupConverter), and reference it.

6. Copy the code file `CSharp/parallel/ParallelExtensionsExtras/TaskSchedulers/StaTaskScheduler.cs` from [DotNet Samples](https://github.com/dotnet/samples)
5. Copy the code file `CSharp/parallel/ParallelExtensionsExtras/TaskSchedulers/StaTaskScheduler.cs` from [DotNet Samples](https://github.com/dotnet/samples)

Create a document processor
---------------------------
Expand Down Expand Up @@ -137,10 +128,10 @@ Enable plug-in
--------------
1. Build our project.
2. Copy the output dll files to:
* Global: a folder you create, named `Plugins` under the folder where the Docfx executable resides.
* Non-global: a folder you create with the name `Plugins` under a template folder. Then run `DocFX build` command with parameter `-t {template}`.
* Global: the Docfx executable directory.
* Non-global: a folder you create with the name `plugins` under a template folder. Then run `DocFX build` command with parameter `-t {template}`.

*Hint*: DocFX can merge templates so create a template that only contains the `Plugins` folder, then run the command `DocFX build` with parameter `-t {templateForRender},{templateForPlugins}`.
*Hint*: DocFX can merge templates so create a template that only contains the `plugins` folder, then run the command `DocFX build` with parameter `-t {templateForRender},{templateForPlugins}`.

Build document
--------------
Expand Down
33 changes: 5 additions & 28 deletions src/Microsoft.DocAsCode.App/Helpers/DocumentBuilderWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,7 @@
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using Microsoft.DocAsCode.Build.ConceptualDocuments;
using Microsoft.DocAsCode.Build.Engine;
using Microsoft.DocAsCode.Build.ManagedReference;
using Microsoft.DocAsCode.Build.ResourceFiles;
using Microsoft.DocAsCode.Build.RestApi;
using Microsoft.DocAsCode.Build.SchemaDriven;
using Microsoft.DocAsCode.Build.TableOfContents;
using Microsoft.DocAsCode.Build.UniversalReference;
using Microsoft.DocAsCode.Common;
using Microsoft.DocAsCode.Plugins;

Expand Down Expand Up @@ -42,7 +35,9 @@ public static void BuildDocument(BuildJsonConfig config, BuildOptions options, T
postProcessorNames = postProcessorNames.Add("SitemapGenerator");
}

using var builder = new DocumentBuilder(s_pluginAssemblies, postProcessorNames);
var pluginAssemblies = templateManager.GetTemplateDirectories().Select(d => Path.Combine(d, "plugins")).SelectMany(LoadPluginAssemblies);

using var builder = new DocumentBuilder(s_pluginAssemblies.Concat(pluginAssemblies), postProcessorNames);
using (new PerformanceScope("building documents", LogLevel.Info))
{
var parameters = ConfigToParameter(config, options, templateManager, baseDirectory, outputDirectory, templateDirectory);
Expand All @@ -52,20 +47,8 @@ public static void BuildDocument(BuildJsonConfig config, BuildOptions options, T

private static IEnumerable<Assembly> LoadPluginAssemblies(string pluginDirectory)
{
var defaultPluggedAssemblies = new List<Assembly>
{
typeof(ConceptualDocumentProcessor).Assembly,
typeof(ManagedReferenceDocumentProcessor).Assembly,
typeof(ResourceDocumentProcessor).Assembly,
typeof(RestApiDocumentProcessor).Assembly,
typeof(TocDocumentProcessor).Assembly,
typeof(SchemaDrivenDocumentProcessor).Assembly,
typeof(UniversalReferenceDocumentProcessor).Assembly,
};
foreach (var assem in defaultPluggedAssemblies)
{
yield return assem;
}
if (!Directory.Exists(pluginDirectory))
yield break;

Logger.LogInfo($"Searching custom plugins in directory {pluginDirectory}...");

Expand All @@ -88,12 +71,6 @@ private static IEnumerable<Assembly> LoadPluginAssemblies(string pluginDirectory
continue;
}

if (defaultPluggedAssemblies.Select(n => n.GetName().Name).Contains(assemblyName))
{
Logger.LogVerbose($"Skipping default plugged assembly: {assemblyName}.");
continue;
}

if (!IsDocfxPluginAssembly(assemblyFile))
{
Logger.LogVerbose($"Skipping non-plugin assembly: {assemblyName}.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,29 @@ public TemplateProcessor GetTemplateProcessor(DocumentBuildContext context, int

private CompositeResourceReader CreateTemplateResource(IEnumerable<string> resources)
{
return new(resources.Select(FindResource).Where(s => s != null));
return new(GetTemplateDirectories(resources).Select(path => new LocalFileResourceReader(path)));
}

public IEnumerable<string> GetTemplateDirectories()
{
return GetTemplateDirectories(_templates);
}

ResourceFileReader? FindResource(string name)
private IEnumerable<string> GetTemplateDirectories(IEnumerable<string> names)
{
foreach (var name in names)
{
var directory = Path.Combine(AppContext.BaseDirectory, "templates", name);
var directory = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "templates", name));
if (Directory.Exists(directory))
{
return new LocalFileResourceReader(directory);
yield return directory;
}

directory = Path.Combine(_baseDirectory, name);
directory = Path.GetFullPath(Path.Combine(_baseDirectory, name));
if (Directory.Exists(directory))
{
return new LocalFileResourceReader(directory);
yield return directory;
}

return null;
}
}

Expand Down
15 changes: 15 additions & 0 deletions test/docfx.Tests/Assets/template/plugins/CustomPostProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Immutable;
using System.Composition;
using Microsoft.DocAsCode.Plugins;

[Export(nameof(CustomPostProcessor), typeof(IPostProcessor))]
public class CustomPostProcessor : IPostProcessor
{
public ImmutableDictionary<string, object> PrepareMetadata(ImmutableDictionary<string, object> metadata) => metadata;

public Manifest Process(Manifest manifest, string outputFolder)
{
File.WriteAllText(Path.Combine(outputFolder, "customPostProcessor.txt"), "customPostProcessor");
return manifest;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Project></Project>
19 changes: 19 additions & 0 deletions test/docfx.Tests/Assets/template/plugins/plugins.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.DocAsCode.Plugins\Microsoft.DocAsCode.Plugins.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Composition" />
</ItemGroup>

</Project>
Binary file not shown.
115 changes: 61 additions & 54 deletions test/docfx.Tests/CompositeCommandTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,61 +31,66 @@ public CompositeCommandTest()
public void TestCommandFromCSCodeToHtml()
{
// Create source file
var sourceCode = @"
namespace Hello{
/// <summary>
/// The class &lt; &gt; > description goes here...
/// </summary>
/// <example>
/// Here is some &lt; encoded &gt; example...
/// > [!NOTE]
/// > This is *note*
///
/// <code>
/// var handler = DateTimeHandler();
/// for (var i = 0; i &lt; 10; i++){
/// date = date.AddMonths(1);
/// }
/// </code>
/// </example>
public class HelloWorld {}}
";
var sourceCode = """
namespace Hello{
/// <summary>
/// The class &lt; &gt; > description goes here...
/// </summary>
/// <example>
/// Here is some &lt; encoded &gt; example...
/// > [!NOTE]
/// > This is *note*
///
/// <code>
/// var handler = DateTimeHandler();
/// for (var i = 0; i &lt; 10; i++){
/// date = date.AddMonths(1);
/// }
/// </code>
/// </example>
public class HelloWorld {}}
""";
var sourceFile = Path.Combine(_projectFolder, "src", "test.cs");
CreateFile(sourceFile, sourceCode, "src");

var docfxJson = $@"{{
""metadata"": [
{{
""src"": ""src/test.cs"",
""dest"": ""api""
}}
],
""build"": {{
""content"": {{
""files"": ""api/*.yml""
}},
""dest"": ""{_outputFolder.ToNormalizedPath()}/site"",
""sitemap"":{{
""baseUrl"": ""https://dotnet.github.io/docfx"",
""priority"": 0.1,
""changefreq"": ""monthly"",
""fileOptions"":{{
""**.yml"": {{
""priority"": 0.3,
""lastmod"": ""1999-01-01""
}},
""**/Hello.yml"": {{
""baseUrl"": ""https://dotnet.github.io/docfx/1"",
""priority"": 0.8,
""changefreq"": ""Daily""
}}
}}
}}
}}
}}";
var docfxJson = $$"""
{
"metadata": [
{
"src": "src/test.cs",
"dest": "api"
}
],
"build": {
"content": {
"files": "api/*.yml"
},
"dest": "{{_outputFolder.ToNormalizedPath()}}/site",
"sitemap":{
"baseUrl": "https://dotnet.github.io/docfx",
"priority": 0.1,
"changefreq": "monthly",
"fileOptions":{
"**.yml": {
"priority": 0.3,
"lastmod": "1999-01-01"
},
"**/Hello.yml": {
"baseUrl": "https://dotnet.github.io/docfx/1",
"priority": 0.8,
"changefreq": "Daily"
}
}
}
}
}
""";

var docfxJsonFile = Path.Combine(_projectFolder, "docfx.json");
File.WriteAllText(docfxJsonFile, docfxJson);
Program.Main(new string[] { docfxJsonFile });
Assert.Equal(0, Program.Main(new string[] { docfxJsonFile }));
var filePath = Path.Combine(_outputFolder, "site", "api", "Hello.HelloWorld.html");
Assert.True(File.Exists(filePath));
var html = new HtmlDocument();
Expand All @@ -95,10 +100,12 @@ public class HelloWorld {}}
var note = html.DocumentNode.SelectSingleNode("//div[@class='NOTE']").InnerHtml;
Assert.Equal("<h5>Note</h5>\n<p>This is <em>note</em></p>", note.Trim());
var code = html.DocumentNode.SelectNodes("//pre/code")[1].InnerHtml;
Assert.Equal(@"var handler = DateTimeHandler();
for (var i = 0; i &lt; 10; i++){
date = date.AddMonths(1);
}".Replace("\r\n", "\n"), code);
Assert.Equal("""
var handler = DateTimeHandler();
for (var i = 0; i &lt; 10; i++){
date = date.AddMonths(1);
}
""".Replace("\r\n", "\n"), code);
var sitemap = Path.Combine(_outputFolder, "site", "sitemap.xml");
Assert.True(File.Exists(sitemap));

Expand Down
22 changes: 22 additions & 0 deletions test/docfx.Tests/DocsetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,26 @@ public static async Task CustomLogo_Override_LogoFromTemplate()

Assert.Equal("<svg>my svg</svg>", outputs["logo.svg"]());
}

[Fact]
public static async Task Load_Custom_Plugin_From_Template()
{
var outputs = await Build(new()
{
["docfx.json"] =
"""
{
"build": {
"content": [{ "files": [ "*.md" ] }],
"template": ["default", "../../Assets/template"],
"dest": "_site",
"postProcessors": ["CustomPostProcessor"]
}
}
""",
["index.md"] = ""
});

Assert.Equal("customPostProcessor", outputs["customPostProcessor.txt"]());
}
}
2 changes: 0 additions & 2 deletions test/docfx.Tests/PdfTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Runtime.CompilerServices;

using Microsoft.DocAsCode.Tests.Common;

using Xunit;

namespace Microsoft.DocAsCode.Tests;
Expand Down