Skip to content

Commit 3edeb4f

Browse files
committed
docs: document script-driven generators
1 parent 7e32f27 commit 3edeb4f

5 files changed

Lines changed: 81 additions & 2 deletions

File tree

docs/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
** xref:extensions/corpus-extensions.adoc[Corpus Extensions]
2727
** xref:extensions/handlebars-extensions.adoc[Handlebars Extensions]
2828
** xref:extensions/data-driven-generators.adoc[Data-Driven Generators]
29+
** xref:extensions/script-driven-generators.adoc[Script-Driven Generators]
2930
** xref:extensions/antora.adoc[Antora Extensions]
3031
** xref:extensions/as-library.adoc[Mr.Docs as a Library]
3132
** xref:extensions/dom-reference.adoc[DOM Reference]

docs/modules/ROOT/pages/extensions/data-driven-generators.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,6 @@ include::example$data-driven-generators/latex/simple.latex[]
246246
----
247247
======
248248

249+
To build the output structure yourself, e.g. one file per namespace or a single aggregated artifact like a search index, hand the whole emit to a script instead of rendering one page per symbol. See xref:extensions/script-driven-generators.adoc[Script-driven generators].
250+
249251

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
= Script-driven generators
2+
3+
A data-driven generator renders one page per symbol from templates. When you need a different output structure, e.g. one file per namespace, or a single artifact aggregated across every symbol, such as a search index, a template generator cannot express it, because the page-per-symbol shape is fixed by the host. A script-driven generator hands the whole emit to a Lua or JavaScript script, which traverses the corpus and writes whatever files it wants. No C++ and no templates are involved.
4+
5+
A generator directory is script-driven when its mrdocs-generator.yml names an entry script:
6+
7+
[source,yaml]
8+
----
9+
script: generator.lua
10+
----
11+
12+
The `script` key holds a path to a Lua (.lua) or JavaScript (.js) file, relative to the generator directory. Naming a script is what distinguishes the two flavors: a manifest with a `script` key is script-driven, otherwise the directory is a data-driven (template) generator. As with template generators, the directory name is the generator id you select with `--generator`.
13+
14+
== The `generate` entry point
15+
16+
The script defines a single entry point, a function named `generate`:
17+
18+
[source,lua]
19+
----
20+
function generate(corpus, output, config, params)
21+
-- ...
22+
end
23+
----
24+
25+
`corpus.symbols` is the array of every symbol. Each symbol carries the same fields the template and helper layers see, plus a flat `_id` string suitable as a stable per-symbol URL fragment.
26+
27+
`output.write(relativePath, contents)` writes one file under the configured output directory, which is the path specified with `--output` on the command line, or with the `output` key in the config file; that's the same location the built-in generators write to. The path is resolved relative to that directory and may not escape it; an absolute path or one that climbs above the output directory is rejected. Parent directories are created as needed.
28+
29+
Because the script owns the output, it also owns what a per-page generator would otherwise do for it: the URLs it emits, and any escaping of the content it writes. The host does not apply an escape map to a script-driven generator's output.
30+
31+
`config` is the resolved configuration: the same object the templates receive, holding every value from the config file and the command line. See xref:configuration/reference.adoc[the configuration reference] for the available keys.
32+
33+
`params` is this generator's own parameters, read from the optional `params:` mapping in its mrdocs-generator.yml. A scalar value is a string (a script coerces numbers or booleans itself); nested mappings and sequences become objects and arrays. It is an empty object when the manifest declares no parameters. For example:
34+
35+
[source,yaml]
36+
----
37+
script: generator.lua
38+
params:
39+
title: API Reference
40+
----
41+
42+
makes `params.title` available to the script.
43+
44+
`config` and `params` are trailing arguments, so a generator that needs neither can omit them, and use `function generate(corpus, output)`.
45+
46+
Both Lua and JavaScript look up `generate` as a global function, so a generator must define one; a value the script returns is not used. Requiring the named global keeps one convention across the two languages and leaves room for a script to expose more than one named entry point later.
47+
48+
Unlike a corpus-transform extension, whose hook is optional, a generator must define a `generate` function: selecting the generator is a request for output, so a missing entry point is an error.
49+
50+
== Example: a search index
51+
52+
This generator emits a single search-index.json aggregating every symbol, an artifact no per-page generator can produce:
53+
54+
[source,lua]
55+
----
56+
-- Quote a string as a JSON value.
57+
local function json_string(s)
58+
s = s:gsub('\\', '\\\\'):gsub('"', '\\"')
59+
return '"' .. s .. '"'
60+
end
61+
62+
function generate(corpus, output)
63+
local entries = {}
64+
for _, sym in ipairs(corpus.symbols) do
65+
local name = sym.name or ""
66+
if name ~= "" then
67+
entries[#entries + 1] =
68+
'{"name":' .. json_string(name) ..
69+
',"url":' .. json_string(sym._id .. ".html") .. "}"
70+
end
71+
end
72+
output.write(
73+
"search-index.json",
74+
"[" .. table.concat(entries, ",") .. "]")
75+
end
76+
----

docs/mrdocs.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@
259259
},
260260
"generator": {
261261
"default": "html",
262-
"description": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The built-in generators include `adoc`, `html`, and `xml`; data-driven generators can be added by dropping a template folder under <addon>/generator/<name>/.",
262+
"description": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The built-in generators include `adoc`, `html`, and `xml`; data-driven generators can be added by dropping a template folder under <addon>/generator/<name>/; script-driven generators instead ship a Lua or JavaScript script that produces the output.",
263263
"title": "Generator used to create the documentation",
264264
"type": "string"
265265
},

src/lib/ConfigOptions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@
443443
{
444444
"name": "generator",
445445
"brief": "Generator used to create the documentation",
446-
"details": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The built-in generators include `adoc`, `html`, and `xml`; data-driven generators can be added by dropping a template folder under <addon>/generator/<name>/.",
446+
"details": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The built-in generators include `adoc`, `html`, and `xml`; data-driven generators can be added by dropping a template folder under <addon>/generator/<name>/; script-driven generators instead ship a Lua or JavaScript script that produces the output.",
447447
"type": "string",
448448
"default": "html"
449449
},

0 commit comments

Comments
 (0)