Module:Documentation
This module is used to generate documentation for modules (e.g. Module:UtilsMarkup) and module-based templates (e.g. Template:Term). The documentation is generated from a specification in the form of a Lua table. This specification can be re-used to parse and validate input, as well as generate TemplateData. It can also be used to generate usage examples. For module functions, one can can specify what the expected output should be for a given example, turning it into an automated unit test.
The advantages of this design are as follows:
- Documentation is standardized.
- The documentation for a module/template is located on the same page as the module code, allowing for both to be edited together at the same time.
- Template documentation is less likely to be incorrect or out of date. For example, there is no chance that the documentation says a parameter is optional when in fact it is required—the code that documents which parameters are required is the same code used to actually verify that required args are present when the template is used.
For documenting simple templates that are not module-based, see Template:Usage and Template:Examples.
This module exports the following functions.Templates
To generate documentation for a module-based template, add this to the template page:
<noinclude>{{#invoke:Documentation|Template}}</noinclude>
And add this to the module page:
<module code>
p.Templates = {
["Template Name"] = <TemplateSpec>
}
return p
Templates
- A map of template names to template specs.
<template name>
[wip]
- Flag to indicate the template is a work in progress.
[description]
Corresponds to the
description
property of TemplateData. This description is displayed in the popup that appears when editing a template in VisualEditor.Must be in plain text, without any wikitext markup.
[purpose]
- Purpose of the template. Displayed in the Purpose section of the template documentation page.
[categories]
- Categories to add to the template page.
[usage]
- General usage information, such as instructions on where to put this template on articles. Placed at the top of the Usage section of the template documentation page.
[storesData]
- For
/Store
templates, use this field to indicate which Data page this template is used on, or simply set totrue
if the storage is not centralized. If present, the template is added to Category:Cargo storage templates. [usesData]
- For templates that use Cargo data, use this field to indicate which page(s) the data is stored on, or simply set to
true
if the storage is not centralized. If present, the template is added to Category:Cargo query templates. [usesModuleData]
- Set to true for templates that rely on /Data pages in the Module namespace.
[format]
- Indicates how the template should be laid out in source.
inline
for single-line templates,block
for multiline templates. Defaults toinline
unlesstableParams
is present. [indent=0]
- Number of spaces to indent block-format parameters.
[boilerplate]
- Directives for how generate boilerplate for the template.
[before]
- Any wikitext entered here will be prepended to the boilerplate
[after]
- Any wikitext entered here will be appended to the boilerplate
[list]
- If true, boilerplate includes Template:List. See Template:Game Rating for example.
[separateRequiredParams]
- If true, two boilerplate tabs will be generated: one with only default parameters, and one with all parameters. Default value is
true
. The option exists and is true by default because it's often useful (e.g. Template:Tabs) but not always (e.g. Template:Armor/Store). Works only for block templates (format = "block"
). Inline templates have thecanOmit
option instead. [hiddenParams]
- Parameters to exclude from boilerplate. See Module:Navbox for example usage.
[disable]
- If true, boilerplate generation is disabled completely. See Template:Cite Book/Documentation for usage.
[tabs]
- To generate multiple tabs of boilerplate, each with a different set of parameters.
[repeatedGroup]
- This is used to make templates that accept "arrays of objects" or "rows" of input. For an example, see Template:Tabs.
name
- The key that the resulting array will be assigned to in the returned argument table.
params
- An array of parameter keys (similar to
paramOrder
), indicating which parameters are repeated. [counts]
- Determines how many times the parameters are repeated in the boilerplate. Each number causes a new tab of boilerplate to be created with that many repetitions. The default value for this field is
{2, 3, 5, 8, 13, 21}
. [allowSingle]
- Allows a repeated parameter to also be specified as a regular parameter. See Module:Wares for example usage.
[paramOrder]
- Array of parameter keys indicating what order they should be presented in.
[params]
Map of the template parameters. Numerical keys indicate positional parameters. String keys indicate named parameters. A special key named
...
indicates a variadic parameter (i.e. a template with a variable number of trailing arguments, such as Template:List).This data can be used by Module:UtilsArg to parse template arguments.
<string|number>
[name]
- Use this to assign names to positional parameters.
[aliases]
- Alternative names for the parameter. Used when renaming a parameter in a high-usage template.
[desc]
- Wikitext description of the parameter.
[placeholder]
- Placeholder to use for argument value when demonstrating template usage. Defaults to
<name>
for positional parameters; empty string for named parameters. [type]
- One of the Extension:TemplateData types.
[required]
- Indicates a required parameter.
[suggested]
- Indicates a suggested parameter.
[deprecated]
- Indicates a parameter that should no longer be used.
[enum]
- An array of allowed values. Optionally, a
reference
key can be added to the Lua table which links to a page listing all the allowed values (see Module:Region#enum for example). [enumDependsOn]
- If the allowed values for this parameter depends on the value of another parameter, specify that parameter name here. Then, instead of
enum
being an array, it can be a function that returns an array. The value of the dependency argument is passed to the function. See Module:Scale for an example. [default]
- Default value for the parameter.
[canOmit]
Omit parameter from generated boilerplate. Use for parameters that cover edge cases and should thefore not be used in normal circumstances. See Template:Game Rating for example.
Works only for inline templates (
format = "inline"
), as block templates by default have two separate boilerplates for minimal required parameters vs. the full set of parameters. See theseparateRequiredParams
option.[inline]
- If true, then the parameter will be printed on the same line as the previous parameter, even if
format
is set to"block"
. See Template:Sequence/Store for example. [trim]
- Indicator to utilsArg.parse that this template argument should be trimmed using utilsString.trim.
[nilIfEmpty]
- Indicator to utilsArg.parse that this template argument should be made nil if it is an empty string, using utilsString.nilIfEmpty.
[split]
- Indicator to utilsArg.parse. If set to
true
, the template argument is treated as a list of commma-separated values, to be turned into an array using utilsString.split. If set to a string, that string will be used as the splitting pattern. [sortAndRemoveDuplicates]
- Indicator to utilsArg.parse that can be used together with
split
andenum
. If true, then the split array will be sorted according to the order of items inenum
. Duplicates and invalid values are be removed.
[examples]
Array of argument tables representing different invocations of the template + an optional
vertical
key. It is possible to add descriptions to specific examples as well. See Template:List for examples.It is also possible to write examples as wikitext strings. This is useful when the example requires some context outside the template itself. (At the time of writing there are no longer uses for this – please add an example here if that changes.)
[vertical=false]
- If false, examples are laid out in a single table (e.g. Template:List). If true, examples are listed one after the other (e.g. Template:Data Table).
[wrapLines=false]
- If true, line wrapping is enabled for all template input shown in <pre> tags. Otherwise, line wrapping is enabled only if the input contains newlines or
vertical
is enabled.
Examples
/TemplateData
Subpages
While it is convenient to define templates specs as a table in the module itself, it becomes a performance issue at scale. These tables, which can become quite large, are initialized each time the module is invoked. When a module is invoked hundreds of times on a page (e.g. Module:Term) this can add unacceptable computing time and memory overhead.
For modules which can be invoked hundreds of times on a single page, the Templates
object should be moved to a subpage called Module:ModuleName/TemplateData
. Module:Documentation will automatically pick up the object from the subpage instead of the module page. If the Templates
object is needed over in the module itself for Module:UtilsArg, then import the subpage into the main module using mw.loadData
so that the tables are initialized only once.
Module:ModuleName/TemplateData
|
Module:ModuleName
|
---|---|
return {
["Template Name"] = <TemplateSpec>
}
|
local p = {}
p.Templates = mw.loadData("Module:ModuleName/TemplateData")
|
This approach is more cumbersome for template/module developers and is recommended only for modules which have noticeable performance issues. You can usually tell if the documentation is causing performance issues by previewing the Link page and seeing if the following function appears in the Profiler data:
init <Module:ModuleName> chunk <Module:ModuleName>
Modules
Usage
To create module documentation, create the page Module:Module Name/Documentation
with the following contents
{{#invoke:Documentation|Module}}
Then on the actual module page, at the end of the script just before the final return, add a Documentation
function to the export table.
<module code>
function p.Documentation()
return {
["Function Name"] = <Function Documentation>
}
end
return p
Note that the schema for Module:Util subpages are somewhat different as they each define only one function. See Module:Util/tables/filter/Documentation/Spec for example.
Documentation
- functionssections
- Map of function names to function documentation. Functions are printed in the order in which they appear in the source code. A function's documentation object has the following properties, depending on whether it is a template function or a module function.
<function name>
- Module functionTemplate function
- A function which is to be invoked by other modules.
[wip]
- Tags the function doc with Template:WIP.
[desc]
- Description of the function. Use only when clarification is needed—usually the param/returns/cases doc speaks for itself.
[params]
- An array of parameter names. Integrates with Schemas.
[_params]
- To be specified for functions with an alternative higher-order function.
[returns]
- A string describing the return value of the function, or an array of such strings if the function returns multiple values
[cases]
- A collection of use cases that double as test cases and documented examples.
[resultOnly]
- When
true
, displays only rendered wikitext as opposed to raw function output. Useful for functions returning strings of complex wikitext. [outputOnly]
- When
true
, displays only the raw output of the function (opposite ofresultOnly
). Enabled by default for functions returning data of type other thanstring
.
- argssnippet
[desc]
- A description of the use case.
[args]
- An array of arguments to pass to the function.
[expect]
The expected return value, which is deep-compared against the actual value to determine pass/fail status. Or, an array of such items if there are multiple return values.
It is also possible to specify a function here to perform a custom assertion other than a simple equality check. See Module:Util/args/parse/Documentation/Spec for example.
[desc]
- A description of the use case.
snippet
- See Module:UtilsTable and Module:UtilsTable/Documentation/Snippets for examples of usage.
[expect]
The expected return value, which is deep-compared against the actual value to determine pass/fail status. Or, an array of such items if there are multiple return values.
It is also possible to specify a function here to perform a custom assertion other than a simple equality check. See Module:Util/args/parse/Documentation/Spec for example.
- A function which is to be invoked by templates using the #invoke parser function.
[wip]
- Tags the function doc with Template:WIP.
[desc]
- Description of the function - which templates should use it and when.
[frameParamsFormat]
- Indicates how the #invoke parameters should be laid out.
[frameParamsOrder]
- Determines the order that
frameParams
should appear in. [frameParams]
- Use this instead of
params
when documenting a template-based function. See Module:Error for example. <number|string>
[name]
- Use this to assign names to positional parameters.
[required]
- Indicates a required parameter.
[enum]
- Indicates which string values are allowed for this parameter.
[default]
- Default value for the parameter.
[desc]
- Description of the parameter.
[inline]
- If true, then the parameter will be printed on the same line as the previous parameter, even if
frameParamsFormat
is set tomultiLine
. See Module:Comment for example. [spaceBefore]
- If true, adds an extra newline before printing the parameter. See Module:Comment for a usage example.
[cases]
- A collection of use cases that double as test cases and documented examples.
[resultOnly]
- When
true
, displays only rendered wikitext as opposed to raw function output. Useful for functions returning strings of complex wikitext. [outputOnly]
- When
true
, displays only the raw output of the function (opposite ofresultOnly
). Enabled by default for functions returning data of type other thanstring
.
sections
- Divides the documentation into sections. See Module:UtilsTable for a usage example.
[heading]
- Section heading
section
- Map of function names to function documentation. Functions are printed in the order in which they appear in the source code. A function's documentation object has the following properties, depending on whether it is a template function or a module function.
<function name>
- Module functionTemplate function
- A function which is to be invoked by other modules.
[wip]
- Tags the function doc with Template:WIP.
[desc]
- Description of the function. Use only when clarification is needed—usually the param/returns/cases doc speaks for itself.
[params]
- An array of parameter names. Integrates with Schemas.
[_params]
- To be specified for functions with an alternative higher-order function.
[returns]
- A string describing the return value of the function, or an array of such strings if the function returns multiple values
[cases]
- A collection of use cases that double as test cases and documented examples.
[resultOnly]
- When
true
, displays only rendered wikitext as opposed to raw function output. Useful for functions returning strings of complex wikitext. [outputOnly]
- When
true
, displays only the raw output of the function (opposite ofresultOnly
). Enabled by default for functions returning data of type other thanstring
.
- argssnippet
[desc]
- A description of the use case.
[args]
- An array of arguments to pass to the function.
[expect]
The expected return value, which is deep-compared against the actual value to determine pass/fail status. Or, an array of such items if there are multiple return values.
It is also possible to specify a function here to perform a custom assertion other than a simple equality check. See Module:Util/args/parse/Documentation/Spec for example.
[desc]
- A description of the use case.
snippet
- See Module:UtilsTable and Module:UtilsTable/Documentation/Snippets for examples of usage.
[expect]
The expected return value, which is deep-compared against the actual value to determine pass/fail status. Or, an array of such items if there are multiple return values.
It is also possible to specify a function here to perform a custom assertion other than a simple equality check. See Module:Util/args/parse/Documentation/Spec for example.
- A function which is to be invoked by templates using the #invoke parser function.
[wip]
- Tags the function doc with Template:WIP.
[desc]
- Description of the function - which templates should use it and when.
[frameParamsFormat]
- Indicates how the #invoke parameters should be laid out.
[frameParamsOrder]
- Determines the order that
frameParams
should appear in. [frameParams]
- Use this instead of
params
when documenting a template-based function. See Module:Error for example. <number|string>
[name]
- Use this to assign names to positional parameters.
[required]
- Indicates a required parameter.
[enum]
- Indicates which string values are allowed for this parameter.
[default]
- Default value for the parameter.
[desc]
- Description of the parameter.
[inline]
- If true, then the parameter will be printed on the same line as the previous parameter, even if
frameParamsFormat
is set tomultiLine
. See Module:Comment for example. [spaceBefore]
- If true, adds an extra newline before printing the parameter. See Module:Comment for a usage example.
[cases]
- A collection of use cases that double as test cases and documented examples.
[resultOnly]
- When
true
, displays only rendered wikitext as opposed to raw function output. Useful for functions returning strings of complex wikitext. [outputOnly]
- When
true
, displays only the raw output of the function (opposite ofresultOnly
). Enabled by default for functions returning data of type other thanstring
.
Tests
The expect
property transforms documentation examples into actual test cases, using a deep equality assertion between the expected output and actual output. If the assertion fails, the page is added to Category:Modules with failing tests.
This module therefore rolls documentation and testing into one. This approach was chosen over ScribuntoUnit and Docbunto for the following reasons:
- Module functionality that is worth testing is worth documenting too, and vice-versa. The specification should be written only once, in one place.
- For modules like Module:UtilsLayout that render a ton of markup, it is not practical to test with coded assertions. In these cases it suffices for module developers to manually check that the examples are outputting correct visuals.
- A module's code, tests, and documentation are tightly coupled. It's much more convenient that they all be on the same page so that they can be edited simultaneously.
- We must sometimes forgo this convenience for performance reasons, however. See #/Spec Subpages.
Data
Category:Module Data can be documented and validated by adding the following to the core module:
Module:ModuleName | Module:ModuleName/Data/Documentation |
---|---|
function p.Data()
return "Should return a representation of the data"
end
-- This part is optional but can be useful to document and validate the data. See "Schemas" below.
function p.Schemas()
return {
Data = { <Schema> }
}
end
|
{{#invoke:Documentation|Module}} |
Schemas
A schema can be used to generate type documentation for function parameters or for module data. Without schemas, it can sometimes be difficult for others to know what input a module expects, given Lua's dynamic type system. This is especially true for functions with table
arguments.
Note for example the parameter documentation for Module:UtilsLayout#tabs:
data
record!-
label
string![tooltip]
[string]
[defaultTab=1]
[number]
The above indicates that:
data
is expected to be a table with two keys:label
andtooltip
.label
is required and of type string.tooltip
is optional and of type string.defaultTab
is an optional number whose default value is 1.
Hovering over the key name indicates its expected type. The types defined in schemas generally correspond to the Lua types, with an additional any
type. The table
type, however, does not exist as a concept in schemas. Instead, there are more specific types which indicate the kind of table in question:
array
— A table with consecutive integer keys starting from 1.record
— A table with fixed set of string keys.map
— A table with any amount of keys, all of the same type. All values are of the same type, but possibly a different type from the keys.
Type | Example |
---|---|
string
|
"Kooloo Limpah"
|
number
|
0
|
boolean
|
true
|
table schema types
| |
array
|
{"Kooloo", "Limpah"}
|
record
|
{
magicWords = {"Kooloo", "Limpah"},
owner = "Tingle",
canSteal = false,
}
|
map
|
{
height = "50px",
width = "auto",
["text-align"] = center,
}
|
Types may be marked with symbols:
Type symbol | Meaning |
---|---|
string!
|
Required |
[string]
|
Optional |
{string}
|
Array |
<string,record>
|
Map with string keys and record values |
string|{string}
|
Either a string or an array of strings |
vararg{string}
|
Indicates that a function receives a variable number of arguments |
Writing Schemas
Schemas are written for function parameters like so:
Module:ModuleName | Module:ModuleName/Documentation |
---|---|
function p.Schemas()
return {
fooFunction = {
param1 = { <Schema> },
param2 = { <Schema> },
},
barFunction = {
param1 = { <Schema> },
param2 = { <Schema> },
},
<...>
}
end
function p.Documentation()
return {
fooFunction = {
params = {"param1", "param2"}
<...>
},
barFunction = {
params = {"param1", "param2"}
<...>
},
<...>
end
|
{{#invoke:Documentation|Module}} |
Schemas can also be made for Module Data as shown below. This generates a schema for the data (see Module:Script/Data/Documentation for example) and also validates that the data matches the schema. Any validation errors that occur will be shown as warnings in the edit preview. Data pages that fail validation are placed in Category:Modules with invalid data.
Module:ModuleName | Module:ModuleName/Data/Documentation |
---|---|
function p.Schemas()
return {
Data = { <Schema> }
<...>
}
end
|
{{#invoke:Documentation|Module}} |
Schemas
- A map of schema names to schemas. Aside from
Data
, each schema name should match a function in the module. <schema name>
[definitions]
- Schema fragments for referencing.
- Primitive SchemaArray SchemaRecord SchemaMap SchemaoneOfallOfReference
[_id]
- An ID to use for referencing.
[required]
- If
true
, the value cannot benil
. [deprecated]
- If
true
, validation fails when this value is present. A deprecation warning is logged. [desc]
- Description of the schema.
[_tabName]
- Assigns a tab name in
oneOf
schemas
type
"string"
,"number"
,"boolean"
, or"any"
[enum]
- An array of values that are considered acceptable, plus an optional
reference
key. [reference]
- A link to a page that lists the accepted values.
[_id]
- An ID to use for referencing.
[required]
- If
true
, the value cannot benil
. [deprecated]
- If
true
, validation fails when this value is present. A deprecation warning is logged. [desc]
- Description of the schema.
[_tabName]
- Assigns a tab name in
oneOf
schemas
[_id]
- An ID to use for referencing.
[required]
- If
true
, the value cannot benil
. [deprecated]
- If
true
, validation fails when this value is present. A deprecation warning is logged. [desc]
- Description of the schema.
[_tabName]
- Assigns a tab name in
oneOf
schemas
type
- The string
"record"
. properties
- An array of schemas for each record entry, plus an additional field
name
for the name of the record entry. name
- The key for the record entry.
[_id]
- An ID to use for referencing.
[required]
- If
true
, the value cannot benil
. [deprecated]
- If
true
, validation fails when this value is present. A deprecation warning is logged. [desc]
- Description of the schema.
[_tabName]
- Assigns a tab name in
oneOf
schemas
type
- The string
"map"
. [keyplaceholder]
- A placeholder for map keys in
map
schemas - defaults to the map keys' type symbol. keys
- The schema for the keys of the map.
values
- The schema for the values of the map.
[_id]
- An ID to use for referencing.
[required]
- If
true
, the value cannot benil
. [deprecated]
- If
true
, validation fails when this value is present. A deprecation warning is logged. [desc]
- Description of the schema.
[_tabName]
- Assigns a tab name in
oneOf
schemas
oneOf
- A table of subschemas. The data must be valid against exactly one of them.
[_id]
- An ID to use for referencing.
[required]
- If
true
, the value cannot benil
. [deprecated]
- If
true
, validation fails when this value is present. A deprecation warning is logged. [desc]
- Description of the schema.
[_tabName]
- Assigns a tab name in
oneOf
schemas
allOf
- An array of subschemas. The data must be valid against all of them.
[_id]
- An ID to use for referencing.
[required]
- If
true
, the value cannot benil
. [deprecated]
- If
true
, validation fails when this value is present. A deprecation warning is logged. [desc]
- Description of the schema.
[_tabName]
- Assigns a tab name in
oneOf
schemas
_ref
- A reference to another part of the schema.
[_hideSubkeys]
- Ignore reference when generating documentation. Use sparingly as there is already a mechanism for determining whether to show referenced schema fragments.
Combining Schemas
It is possible to combine schemas using oneOf
and allOf
.
Keyword | Schema | Example Data |
---|---|---|
oneOf
|
{
type = "array",
items = {
oneOf = {
{ type = "string" },
{ type = "number" },
},
}
}
|
{ 1, 2, "Fooloo Limpah", 3, 4}
|
allOf
|
{
allOf = {
{
type = "record",
properties = {
{
name = "foo",
type = "string",
},
},
},
{
type = "array",
items = { type = "number" },
}
},
}
|
{1, 2, 3, foo = "bar"}
|
The combination keywords are often used along with references to maximize reusability.
Schema References
References allow you to reuse parts of the schema in multiple places. There are two ways of referencing schema fragments: using definitions
and using the _id
property.
Reference Type | Schema | Example Data |
---|---|---|
definitions
|
{
type = "array",
items = {
oneOf = {
_ref = "#/definitions/foo",
_ref = "#/definitions/bar",
},
}
definitions = {
foo = {
type = "string",
},
bar = {
type = "number",
},
}
}
|
{ 1, 2, "Fooloo Limpah", 3, 4}
|
_id
|
{
type = "array",
items = {
oneOf = {
{
_id = "#foo",
type = "string"
},
{
type = "array",
items = { _ref = "#foo" }
}
},
}
}
|
{ "foo", "bar", { "baz", "quux" } }
|
/Spec
Subpages
While it is convenient for development to have the Documentation
and Schema
functions on the same module page as the implementation code, it doesn't scale well when the module is used on many pages. Any change to the documentation will trigger unnecessary job queue entries for all the pages that use the module, when only the module's documentation page is actually affected. This becomes a problem for modules linked by thousands of pages.
Instead, you can put the p.Documentation
and p.Schema
functions on a /Spec
subpage under the /Documentation
subpage (e.g. Module:Term/Documentation/Spec). On the /Documentation
page, change the invoke to:
{{#invoke:Documentation|ModuleSpec}}
The /Spec
page is only linked by the /Documentation
page above it and can therefore be edited without causing any long job queues. The downside is that you lose the ability to edit the documentation/tests at the same time as the implementation, which can be troublesome. Generally, it's only worthwhile to separate them for modules that are linked by more than a few hundred pages.
Examples
- Module:Language/Data/Documentation
- Module:Franchise/Documentation
- Module:Util/tables/filter/Documentation
- Anything in Category:Module Documentation
Altogether the following modules use every available schema feature. Module:UtilsSchema has test cases that show how the validation works.