diff --git a/README.md b/README.md index e00eb49..a3f8cb2 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,21 @@ # docfx-tools -A docfx template for package documentation, patching the modern template to provide stylesheets and scripts for rendering custom workflow containers with copy functionality. +A repository of docfx tools for Bonsai package documentation: +- Docfx Workflow Container template patching the modern template to provide stylesheets and scripts for rendering custom workflow containers with copy functionality. +- Docfx API TOC template that groups nodes by operator type in the table of contents(TOC) on API pages. +- Powershell Scripts that automate several content generation steps for package documentation websites. -## How to use +## How to include -To include this template in a docfx website, first clone this repository as a submodule: +To include this repo in a docfx website, first clone this repository as a submodule: ``` git submodule add https://github.com/bonsai-rx/docfx-tools bonsai ``` -Then modify `docfx.json` to include the template immediately after the modern template: +## Using Workflow Container Template + +Modify `docfx.json` to include the template immediately after the modern template: ```json "template": [ @@ -38,12 +43,27 @@ export default { } } ``` +## Using API TOC Template + +The local installation of docfx needs to be updated to >= v2.77.0. -## Powershell Scripts +```ps1 +dotnet tool update docfx +``` -This repository also provides helper scripts to automate several content generation steps for package documentation websites. +Modify `docfx.json` to include the api-toc template (note both the workflow container and API TOC template have to be added separately). + +```json +"template": [ + "default", + "modern", + "bonsai/template", + "bonsai/template/apitoc", + "template" +] +``` -### Exporting workflow images +## Powershell Scripts - Exporting workflow images Exporting SVG images for all example workflows can be automated by placing all `.bonsai` files in a `workflows` folder and calling the below script pointing to the bin directory to include. A bonsai environment is assumed to be available in the `.bonsai` folder in the repository root. diff --git a/template/apitoc/ManagedReference.overwrite.js b/template/apitoc/ManagedReference.overwrite.js new file mode 100644 index 0000000..ff7ef9d --- /dev/null +++ b/template/apitoc/ManagedReference.overwrite.js @@ -0,0 +1,5 @@ +exports.getOptions = function (model) { + return { + isShared: true + }; +} \ No newline at end of file diff --git a/template/apitoc/toc.extension.js b/template/apitoc/toc.extension.js new file mode 100644 index 0000000..0a72c0c --- /dev/null +++ b/template/apitoc/toc.extension.js @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +function defineOperatorType(model){ + // Define Bonsai operator types in documentation by checking for an explicit Category tag. If the class does not provide one, + // check the inheritance tree of the class. + + const checkForCategory = (category) => model.syntax?.content[0].value.includes(`[WorkflowElementCategory(ElementCategory.${category})]`); + const checkInheritance = (inheritance) => model.inheritance?.some(inherited => inherited.uid.includes(inheritance)); + + source = checkForCategory('Source') || checkInheritance('Bonsai.Source'); + sink = checkForCategory('Sink') || checkInheritance('Bonsai.Sink') || checkInheritance('Bonsai.IO.StreamSink') || checkInheritance('Bonsai.IO.FileSink'); + combinator = checkForCategory('Combinator') || checkInheritance('Bonsai.Combinator') || checkInheritance('Bonsai.WindowCombinator'); + transform = checkForCategory('Transform') || checkInheritance('Bonsai.Transform') || checkInheritance('Bonsai.Transform'); + + let operatorType = {} + operatorType.type = sink ? 'sink' : source ? 'source' : transform ? 'transform' : combinator ? 'combinator' : false ; + + return operatorType; +} + +/** + * This method will be called at the start of exports.transform in toc.html.js and toc.json.js + */ +exports.preTransform = function (model) { + + // Checks and applies TOC customisation only to API page + if (model._key === 'api/toc.yml') { + + //Setups operator mapping + const typeIndexMap = { + 'source': 0, + 'transform': 0, + 'sink': 0, + 'combinator': 0 + }; + + // Iterates through each namespace for packages with multiple namespaces + for (namespace of model.items) { + if (namespace.items) { + itemsItemsLength = namespace.items.length; + + // Setups operator categories + let items = [{ + 'name': 'Operators', + 'items': []}, { + 'name': 'Classes', + 'items': []}, { + 'name': 'Enums', + 'items': [] + }]; + + // Iterates through each item in the namespace and sorts them into categories + for (let i = 0; i < itemsItemsLength; i++) { + globalYml = '~/api/' + namespace.items[i].topicUid + '.yml'; + if (model.__global._shared[globalYml] && model.__global._shared[globalYml].type === 'class'){ + operatorType = defineOperatorType(model.__global._shared[globalYml]); + + const index = typeIndexMap[operatorType.type] + if (index !== undefined) { + items[index].items.push(namespace.items[i]); + } + else { + items[1].items.push(namespace.items[i]); + } + } + if (model.__global._shared[globalYml] && model.__global._shared[globalYml].type === 'enum'){ + items[2].items.push(namespace.items[i]); + } + } + + // Filters out empty TOC categories and sets namespace TOC items + items = items.filter(item => item.items.length > 0); + namespace.items = items; + } + } + } + return model; +} + +/** + * This method will be called at the end of exports.transform in toc.html.js and toc.json.js + */ +exports.postTransform = function (model) { + return model; +}