Hooks
Hooks let you inject custom logic into a parser or formatter without modifying the action itself. They run automatically at the named steps of an action’s pipeline, so you can trim a description, rewrite a code block, fetch extra data, or upload output — all from a separate file.
How hooks work
Section titled “How hooks work”Every step in an action’s pipeline has a name (e.g. "Format function",
"Extract tags", "Finalize"). For each step, BeDoc looks for matching hook
methods and runs them around it:
before$<step>runs just before the step.after$<step>runs just after the step.
The <step> part is the camelCase of the step name: "Format function" →
before$formatFunction / after$formatFunction; "Extract tags" →
after$extractTags; "Finalize" → after$finalize.
You don’t call hooks — BeDoc invokes them for you. If no hook matches a step, nothing happens.
Hooks are mutation-based
Section titled “Hooks are mutation-based”The two signatures:
// before$<step>: receives the context heading into the step. Mutate it.before$formatFunction = ctx => { // ctx is the function about to be rendered}
// after$<step>: receives (ctx, result) — the step's input and its output.after$formatFunction = (ctx, result) => { // mutate `result` (or `ctx`) in place; returning a value does nothing}Because of this, a transform must mutate something mutable. To change a
formatter’s output you mutate the array on the result of a per-item step (e.g.
result.formatted in after$formatFunction) — you cannot do it in
after$finalize, whose result is an immutable string.
A hook file
Section titled “A hook file”A hook file exports a Parse class, a Format class, or both — matching the
action kind it should attach to. BeDoc instantiates them with new Parse() /
new Format().
export class Parse { // Strip @example tags during parsing — runs after the parser's // "Extract tags" step. after$extractTags = ctx => { delete ctx.tag?.example }}
export class Format { // Trim blank lines from each function's description before it is rendered. before$formatFunction = ctx => { const description = ctx.description
if(Array.isArray(description)) { while(description.length && !description.at(0)?.trim()) description.shift()
while(description.length && !description.at(-1)?.trim()) description.pop() } }
// Rewrite Markdown code fences to Wikitext syntaxhighlight, in place, // on the rendered sections of each function. after$formatFunction = (ctx, result) => { if(!Array.isArray(result?.formatted)) return
result.formatted = result.formatted.map(section => section.replace( /```c\n([\s\S]+?)```/g, "<syntaxhighlight lang=\"c\">\n$1</syntaxhighlight>\n", ), ) }}Point BeDoc at it with --hooks:
bedoc -l lpc -f wikitext -i "src/**/*.c" -o docs --hooks ./my-hooks.jsLifecycle hooks
Section titled “Lifecycle hooks”Beyond per-step hooks, a hook class may define setup and cleanup, which run
once at the start and end of a run:
export class Format { setup = async items => { // `items` is the array of things to be processed (e.g. the functions). // Use it for one-time preparation. }
cleanup = async items => { // One-time teardown after all items are processed. }}Best practices
Section titled “Best practices”- Mutate, don’t return. Change the context/result in place; returns are ignored.
- Pick the right step. To alter output, hook a step whose context is a
mutable object you can change (e.g.
after$formatFunction). - Guard your access. Optional fields may be absent — use
?.and array checks before mutating. - Keep hooks fast. They run inside the per-file pipeline; the
--hookTimeout(default5000ms) bounds each hook’s execution. - Match the kind. Put parser hooks on
class Parse, formatter hooks onclass Format.
