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

Add API template #9

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
138ce43
Initial commit of template files
banchan86 Aug 8, 2024
3053524
Delete files not required for API template
banchan86 Aug 14, 2024
3e629e3
Add refactored Bonsai and Mref js extensions
banchan86 Aug 14, 2024
2bd0687
Add back required mref.overwrite.js
banchan86 Aug 14, 2024
0d08ec1
Remove OE specific code from define inputs and outputs
banchan86 Aug 14, 2024
45e03d4
Add missing source link for enums
banchan86 Aug 15, 2024
bebd2e0
Simplify enum processing and strip OE specific code
banchan86 Aug 15, 2024
9a1f75d
Strip OE specific suboperator functions
banchan86 Aug 15, 2024
428370d
Strip inherited members in properties table
banchan86 Aug 15, 2024
56accf5
Revert "Strip inherited members in properties table"
banchan86 Aug 15, 2024
18a26ab
Add doc comments for inherited members property table
banchan86 Aug 15, 2024
b6b75e6
Delete unnecessary conceptual markdown template
banchan86 Aug 15, 2024
6c37c73
Add images and modified code for input/output diagram
banchan86 Aug 15, 2024
c5e70d7
Strip OE specific code from class partial template
banchan86 Aug 15, 2024
1c8e075
Remove operator tables from diagram partial template
banchan86 Aug 15, 2024
a575f78
Replace OE with Bonsai in variable names
banchan86 Aug 15, 2024
10337fd
Remove dataframe ref in defineinputoutput
banchan86 Aug 16, 2024
031e25d
Minor code cleanup, finished conversion of template
banchan86 Aug 16, 2024
1094df6
Add relationships to classes/enums
banchan86 Aug 16, 2024
7904709
Add constructors to classes
banchan86 Aug 16, 2024
6e2ff2a
Refactor defineOperatorType function
banchan86 Aug 19, 2024
2078a6f
Refactor some functions
banchan86 Aug 20, 2024
ebcef62
Refactor functions, add styles.css for template table formatting
banchan86 Aug 22, 2024
a861834
Surface links to Bonsai docs for observables and operators
banchan86 Aug 22, 2024
3000e21
Remove constructors from API pages
banchan86 Aug 22, 2024
74dbb7a
Update readme with installation instructions
banchan86 Aug 22, 2024
5ee6e60
Update README with instructions for operator workflow containers
banchan86 Aug 22, 2024
968ca4a
Update README after decoupling API TOC from API template
banchan86 Aug 23, 2024
0f97d2e
Refactor IO diagram, reduce size
banchan86 Sep 22, 2024
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
47 changes: 40 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
# 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.
- Docfx API template that revamps the API page to enhance user-friendliness.
- 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": [
Expand Down Expand Up @@ -38,12 +44,39 @@ export default {
}
}
```
## Using API template

Modify `docfx.json` to include the api template in the template section (note both the workflow container and API template have to be added separately).

```json
"template": [
"default",
"modern",
"bonsai/template",
"bonsai/template/api",
"template"
]
```
In addition, the images and custom css styles need to be added to the resources section.

## Powershell Scripts
```json
"resource": [
{
"files": [
"logo.svg",
"favicon.ico",
"images/**",
"workflows/**",
"bonsai/template/api/images/**",
"bonsai/template/api/styles/**"
]
}
]
```
To add individual operator workflows for the API pages, open Bonsai, add the operator, and save each individual operator workflow as `OperatorName.bonsai` (case sensitive) in `docs/workflows/operators`.

This repository also provides helper scripts to automate several content generation steps for package documentation websites.

### 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.

Expand Down
186 changes: 186 additions & 0 deletions template/api/ManagedReference.extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// 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.
function defineOperatorType(model){
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 function is important for stripping the extra line that is present in some fields
// replace last instance of '<p' with '<p style="margin-bottom:0;"'
function removeBottomMargin(str) {
return str
.split('').reverse().join('')
.replace('<p'.split('').reverse().join(''), '<p style="margin-bottom:0;"'.split('').reverse().join(''))
.split('').reverse().join('');
}

// Strip IObservable and replace 'TSource' with 'Anything'
// Added a null check to return an empty string to avoid undefined errors in new refactored code
// Added links to Bonsai user guide on operators
function replaceIObservableAndTSource(str){
const observableLink = '<a href="https://bonsai-rx.org/docs/articles/observables.html">Observable</a>'
if (!str) {
return '';
}
else if (str.includes('IGroupedObservable')){
const re = new RegExp('<a.*IGroupedObservable.*&lt;');
str = str.replace(re, '').replace('&gt;','').replace('&gt;','').replace('TSource', observableLink);
}
else if (str.includes('IObservable')){
const re = new RegExp('<a.*IObservable.*&lt;');
str = str.replace(re, '').replace('&gt;','').replace('TSource', observableLink);
// can't combine re and '&gt;' into the same regex and do replaceAll because some classes have have two '&gt' in which case one of them is necessary
}
return str;
}

// this function is a revised version of cris's refactored function that restores the original values (specname, description)
// which were removed
function defineInputsAndOutputs(model){
overloads = model.children
.filter(child => child.name[0].value.includes('Process') || child.name[0].value.includes('Generate'))
.map(child => ({
'description': [child.summary, child.remarks].join(''),
'input': {
'specName': replaceIObservableAndTSource(child.syntax?.parameters[0].type.specName[0].value),
'description': removeBottomMargin([child.syntax?.parameters[0].description, child.syntax?.parameters[0].remarks].join(''))
},
'output': {
'specName': replaceIObservableAndTSource(child.syntax.return.type.specName[0].value),
'description': removeBottomMargin([child.syntax.return.description, child.syntax.return.remarks].join('')),
}
}))
.map(item => {
// Remove input if it's empty
if (!item.input.specName && !item.input.description) {
delete item.input;
}
return item;
});
return overloads;
}

// extracts enums so that they can be expanded in the properties table
function processChildProperty(child, sharedModel) {
const enumFields = sharedModel[`~/api/${child.syntax.return.type.uid}.yml`]?.type === 'enum' ?
extractEnumData(sharedModel[`~/api/${child.syntax.return.type.uid}.yml`]) :
[];
return {
'name': child.name[0].value,
'type': child.syntax.return.type.specName[0].value,
'propertyDescription': {
'text': enumFields.length > 0
? [child.summary, child.remarks].join('')
: removeBottomMargin([child.summary, child.remarks].join('')),
'hasEnum': enumFields.length > 0,
'enum': enumFields,
}
}
}

function extractPropertiesData(model, sharedModel) {
return model?.children
.filter(child => child.type === 'property' && child.syntax)
.map(child => processChildProperty(child, sharedModel));
}

function extractPropertiesFromInheritedMembersData(model, sharedModel) {
return model.inheritedMembers
.filter(inheritedMember => inheritedMember.type === 'property')
.map(inheritedMember => {
return processChildProperty(
sharedModel[`~/api/${inheritedMember.parent}.yml`].children.find(inheritedMemberChild => inheritedMemberChild.uid === inheritedMember.uid),
sharedModel
);
});
}


// Properties are usually already listed in declaration order which mirrors Bonsai UI.
// However a bug in docfx messes up properties that have a numeric endvalue ie Device10 < Device2
// and this function fixes that.
function sortPropertiesData(properties) {
return properties.sort((a, b) => {
const regex = /\D+|\d+$/g;

// Extract parts for property 'a'
const [prefixA, numberA] = a.name.match(regex);
const numA = Number(numberA);

// Extract parts for property 'b'
const [prefixB, numberB] = b.name.match(regex);
const numB = Number(numberB);

// If prefix is the same, compare numbers
if (prefixA == prefixB) {
return numA - numB;
}
});
}

// While enum fields can be accessed directly using the mustache template, this function is
// still important for stripping the extra line that is present in the summary/remarks field
function extractEnumData(model){
return model.children
.filter(child => child.type === 'field')
.map(child => ({
'field&value': child.syntax.content[0].value,
'enumDescription': removeBottomMargin([child.summary, child.remarks].join(''))
}));
}

/**
* This method will be called at the start of exports.transform in ManagedReference.html.primary.js
*/
exports.preTransform = function (model) {

model.bonsai = {};

model.bonsai.description = [model.summary, model.remarks].join('');

operatorType = defineOperatorType(model);

if (operatorType.type){
model.bonsai.operatorType = operatorType.type;
model.bonsai.showWorkflow = true
operators = defineInputsAndOutputs(model);
model.bonsai.operators = operators;
}

if (model.type === 'class') {
properties = sortPropertiesData([
...extractPropertiesData(model, model.__global._shared),
...extractPropertiesFromInheritedMembersData(model, model.__global._shared),
]);
if (properties.length > 0){
model.bonsai.hasProperties = true;
model.bonsai.properties = properties
}
}

else if (model.type === 'enum') {
model.bonsai.enumFields = extractEnumData(model);
model.bonsai.hasEnumFields = true;
}
return model;
}

/**
* This method will be called at the end of exports.transform in ManagedReference.html.primary.js
*/
exports.postTransform = function (model) {
return model;
}
7 changes: 7 additions & 0 deletions template/api/ManagedReference.html.primary.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}

{{!master(layout/_master.tmpl)}}

{{>partials/class}}

{{>partials/enum}}
5 changes: 5 additions & 0 deletions template/api/ManagedReference.overwrite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
exports.getOptions = function (model) {
return {
isShared: true
};
}
67 changes: 67 additions & 0 deletions template/api/images/combinator-operator.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading