-
Notifications
You must be signed in to change notification settings - Fork 50
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 myst:template
directive for documentation
#1209
Merged
Merged
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
fd46e3d
Add theme options to docs
choldgraf 3368608
Comments
choldgraf b03aa02
Use function
choldgraf c2c3c33
Document actions and nav
choldgraf b34d680
docs: add template directive
agoose77 9f34ed9
refactor: drop unused code
agoose77 32d47b1
Document plugins
choldgraf acfeaf2
Document theme options a bit more
choldgraf 96d8b08
docs: add helpful comments on the template plugin
agoose77 File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import { u } from 'unist-builder'; | ||
import { mystParse } from 'myst-parser'; | ||
|
||
/** | ||
* Create a documentation section for a directive | ||
* | ||
* @type {import('myst-common').DirectiveSpec} | ||
*/ | ||
const mystTemplate = { | ||
name: 'myst:template', | ||
options: { | ||
kind: { | ||
type: String, | ||
}, | ||
'full-title': { | ||
type: Boolean, | ||
}, | ||
'heading-depth': { | ||
type: Number, | ||
}, | ||
}, | ||
arg: { | ||
type: String, | ||
required: true, | ||
}, | ||
run(data, vfile) { | ||
return [ | ||
u( | ||
'myst-template-ref', | ||
{ | ||
template: data.arg, | ||
kind: data.options?.kind ?? 'site', | ||
fullTitle: data.options?.['fullTitle'] ?? false, | ||
headingDepth: data.options?.['heading-depth'] ?? 2, | ||
}, | ||
[], | ||
), | ||
]; | ||
}, | ||
}; | ||
|
||
let _promise = undefined; | ||
|
||
|
||
function slugify(id) { | ||
return id.replaceAll('/', '-'); | ||
} | ||
|
||
function createOption(template, option) { | ||
if (!option) return []; | ||
const def = [ | ||
u('definitionTerm', { identifier: `template-${slugify(template.id)}-${slugify(option.id)}` }, [ | ||
u('strong', [u('text', option.id)]), | ||
...(option.type | ||
? [ | ||
u('text', ' ('), | ||
u('emphasis', [u('text', `${option.type}${option.required ? ', required' : ''}`)]), | ||
u('text', ')'), | ||
] | ||
: []), | ||
]), | ||
]; | ||
|
||
if (option.description) { | ||
def.push( | ||
u( | ||
'definitionDescription', | ||
option.description ? mystParse(option.description).children : [u('text', 'No description')], | ||
), | ||
); | ||
} | ||
return def; | ||
} | ||
|
||
async function loadFromTemplateMeta(url) { | ||
const response = await fetch(url); | ||
return await response.json(); | ||
} | ||
|
||
async function loadByTemplateKind(url) { | ||
const response = await fetch(url); | ||
const { items } = await response.json(); | ||
return await Promise.all(items.map((item) => loadFromTemplateMeta(item.links.self))); | ||
} | ||
|
||
async function loadTemplates() { | ||
// Load top-level list of templates | ||
const response = await fetch(`https://api.mystmd.org/templates/`); | ||
const { links } = await response.json(); | ||
// Load all the top-level kinds | ||
return (await Promise.all(Object.values(links).map(loadByTemplateKind))).flat(); | ||
} | ||
|
||
const PARTIAL_TEMPLATE_REGEX = /^[a-zA-Z0-9_-]+$/; | ||
const TEMPLATE_REGEX = /^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+$/; | ||
const FULL_TEMPLATE_REGEX = /^(site|tex|typst|docx)\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_-]+)$/; | ||
|
||
function templateTransform(opts, utils) { | ||
return async (mdast) => { | ||
if (_promise === undefined) { | ||
_promise = loadTemplates(); | ||
} | ||
|
||
let templates; | ||
try { | ||
templates = await _promise; | ||
} catch (err) { | ||
throw new Error('Error loading template information from https://api.mystmd.org'); | ||
} | ||
|
||
utils.selectAll('myst-template-ref', mdast).forEach((node) => { | ||
const templateName = node.template; | ||
let resolvedTemplateName; | ||
if (templateName.match(PARTIAL_TEMPLATE_REGEX) && node.kind !== undefined) { | ||
resolvedTemplateName = `${node.kind}/myst/${templateName}`; | ||
} else if (templateName.match(TEMPLATE_REGEX) && node.kind !== undefined) { | ||
resolvedTemplateName = `${node.kind}/${templateName}`; | ||
} else if (templateName.match(FULL_TEMPLATE_REGEX)) { | ||
resolvedTemplateName = templateName; | ||
} else { | ||
throw new Error(`Could not find template with name ${templateName}`); | ||
} | ||
|
||
// Match the name | ||
const template = templates.find((template) => template.id === resolvedTemplateName); | ||
const slug = slugify(template.id); | ||
|
||
const [_, kind, namespace, name, ...rest] = template.id.match(FULL_TEMPLATE_REGEX); | ||
const title = node.fullTitle ? template.id : name; | ||
const heading = u('heading', { depth: node.headingDepth, identifier: `template-${slug}` }, [ | ||
u('inlineCode', title), | ||
u('text', ' template'), | ||
]); | ||
|
||
const options = (template.options ?? {}) | ||
.map((option) => createOption(template, option)) | ||
.flat(); | ||
const list = u('definitionList', [ | ||
u('definitionTerm', { identifier: `template-${slug}-opts` }, [ | ||
u('strong', [u('text', 'Options')]), | ||
]), | ||
options.length > 0 | ||
? u('definitionDescription', [u('definitionList', options)]) | ||
: u('definitionDescription', [u('text', 'No options')]), | ||
]); | ||
const doc = template.description ? mystParse(template.description).children : []; | ||
const link = { | ||
type: 'link', | ||
url: template.links.source, | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'Source', | ||
}, | ||
], | ||
}; | ||
node.children = [heading, ...doc, list, link]; | ||
}); | ||
}; | ||
} | ||
|
||
const mystTemplateTransform = { | ||
plugin: templateTransform, | ||
stage: 'document', | ||
}; | ||
|
||
/** | ||
* @type {import('myst-common').MystPlugin} | ||
*/ | ||
const plugin = { | ||
name: 'MyST Template Documentation Plugins', | ||
author: 'Angus Hollands', | ||
license: 'MIT', | ||
directives: [mystTemplate], | ||
roles: [], | ||
transforms: [mystTemplateTransform], | ||
}; | ||
|
||
export default plugin; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure that all concurrent builds have access to this at the same time by setting the global promise before resolving it.