Zelda Wiki

Want to contribute to this wiki?
Sign up for an account, and get started!

Come join the Zelda Wiki community Discord server!

READ MORE

Zelda Wiki
Register
No edit summary
No edit summary
Line 532: Line 532:
 
end
 
end
 
end
 
end
  +
for _, param in ipairs(data.params) do
 
  +
if type(param.required) == "string" then
  +
param.required = true
  +
end
  +
end
 
for i = 1, repeatedGroupCount do
 
for i = 1, repeatedGroupCount do
 
for _, paramName in ipairs(repeatedGroupParams) do
 
for _, paramName in ipairs(repeatedGroupParams) do
Line 555: Line 559:
 
table.insert(data.paramOrder, "_")
 
table.insert(data.paramOrder, "_")
 
end
 
end
 
 
local templateData = mw.getCurrentFrame():extensionTag("templatedata", mw.text.jsonEncode(data))
 
local templateData = mw.getCurrentFrame():extensionTag("templatedata", mw.text.jsonEncode(data))
 
local html = mw.html.create("div")
 
local html = mw.html.create("div")

Revision as of 12:44, 1 August 2020


local p = {}
local h = {}

local ListPages = require("Module:List Pages")
local i18n = require("Module:I18n")
local s = i18n.getString
local utilsArg = require("Module:UtilsArg")
local utilsLayout = require("Module:UtilsLayout")
local utilsMarkup = require("Module:UtilsMarkup")
local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")

local FORMATS = {
	inline = {
		templateData = "{{_|_= _}}",
		argSeparator = "|",
		afterLastArg = "",
	},
	block = {
		templateData = "{{_\n|_= _\n}}",
		argSeparator = "\n%s|",
		afterLastArg = "\n",
	}
}

local VARARG_KEY = "..."

function p.Template(frame)
	local title = mw.title.getCurrentTitle()
	local templateName = frame.args.template or title.text
	if title.subpageText == "Documentation" then
		templateName = title.baseText
	end
	local moduleName = frame.args.module or title.baseText
	return p.printTemplateDoc(templateName, moduleName)
end

function p.Examples(frame)
	local args = frame:getParent().args
	local examples = h.examplesFromFrame(args)
	return p.printExamples(examples)
end

function p.printTemplateDoc(templateName, moduleName)
	moduleName = moduleName or templateName
	local templateSpec = require("Module:" .. moduleName).Templates[templateName]
	utilsArg.schemaValidate(p.Schemas.TemplateSpec, "TemplateSpec", templateSpec, templateName)
	local result = ""
	if templateSpec.wip then
		result = result .. mw.getCurrentFrame():expandTemplate({title = "UC"})
	end
	local result = result .. "__TOC__\n"
	if templateSpec.purpose then
		result = result .. utilsMarkup.heading(2, s("heading.purpose"))
		result = result .. mw.getCurrentFrame():preprocess(templateSpec.purpose) .. "\n"
	end
	result = result .. utilsMarkup.heading(2, s("heading.usage"))
	if templateSpec.usesData then
		local dataSource
		if type(templateSpec.usesData) == "table" then
			dataSource = ListPages.main(templateSpec.usesData)
		else
			dataSource = utilsMarkup.link(templateSpec.usesData)
		end
		result = result .. utilsMarkup.italic(s("usesData", {dataSource = dataSource})) .. "\n"
	end
	if templateSpec.usesModuleData then
		local dataSource = utilsMarkup.link("Module:" .. moduleName .. "/Data")
		result = result .. utilsMarkup.italic(s("usesModuleData", {dataSource = dataSource})) .. "\n"
	end
	if templateSpec.storesData then
		local dataSource = utilsMarkup.link(templateSpec.storesData)
		result = result .. utilsMarkup.italic(s("storesData", {dataSource = dataSource})) .. "\n"
	end
	result = result .. p.printUsage(templateName, templateSpec)
	if templateSpec.examples then
		local examples = h.examplesFromSpec(templateName, templateSpec)
		result = result .. utilsMarkup.heading(3, s("heading.examples")) 
		result = result .. p.printExamples(examples)
	end
	result = result .. h.templateData(templateSpec)
	result = result .. h.categories(templateSpec)
	return result
end

function p.printUsage(templateName, templateSpec)
	local params = {}
	local paramOrder = templateSpec.paramOrder
	local format = templateSpec.format
	local positionalParamCount = 0
	local hasVarArg = false
	local repeatedParams = templateSpec.repeatedGroup and templateSpec.repeatedGroup.params or {}
	for k, v in pairs(templateSpec.params) do
		if type(k) == "number" then
			positionalParamCount = positionalParamCount + 1
		elseif k == VARARG_KEY then
			hasVarArg = true
		end
			
		if utilsTable.keyOf(repeatedParams, k) then
			-- do nothing, repeated params processed separately
		else
			local i = paramOrder and utilsTable.keyOf(paramOrder, k) or (#params + 1)
			params[i] = utilsTable.merge({}, v, { 
				param = k 
			})
		end
	end
	local repeatedGroup = {}
	for _, param in ipairs(repeatedParams) do
		repeatedGroup[#repeatedGroup + 1] = utilsTable.merge({}, templateSpec.params[param], {
			param = param
		})
	end
	
	local format = FORMATS[templateSpec.format]
	local result = ""
	local boilerplateOptions = utilsTable.merge({}, templateSpec.boilerplate or {}, {
		repeatedGroupCount = templateSpec.repeatedGroup and templateSpec.repeatedGroup.count
	})
	if positionalParamCount > 0 or hasVarArg then
		result = result .. utilsLayout.tabs({
			{
				label = s("tabs.syntax"),
				content = h.syntax(templateName, params, repeatedGroup, format, templateSpec.indent)
			},
			{
				label = s("tabs.boilerplate"),
				content = h.boilerplate(templateName, params, repeatedGroup, format, templateSpec.indent, boilerplateOptions)
			},
		})
	else
		result = h.boilerplate(templateName, params, repeatedGroup, format, templateSpec.indent, boilerplateOptions)
	end
	params = utilsTable.concat(params, repeatedGroup)
	result = result .. h.params(params, positionalParamCount)
	return result
end

function p.printExamples(examples)
	for i, example in ipairs(examples) do
		local input = mw.text.trim(example.input)
		if string.find(input, "\n") or examples.vertical then
			input = utilsMarkup.pre(input, {
				wrapLines = false
			})
		else
			input = utilsMarkup.code(mw.text.nowiki(input))
		end
		if example.output then
			local output, categories = utilsMarkup.stripCategories(example.output)
			local output = utilsMarkup.killBacklinks(output)
			local categoryList = utilsMarkup.bulletList(categories)
			examples[i] = {
				input = input,
				output = output,
				categoryList = categoryList,
				desc = example.desc
			}
		else
			examples[i] = {
				input = input,
				desc = example.desc
			}
		end
	end
	
	if not examples.vertical then
		local rows = {}
		for _, example in ipairs(examples) do
			if example.desc then
				table.insert(rows, {
					{ 
						header = true, 
						colspan = -1, 
						styles = {
							["text-align"] = "left"
						},
						content = example.desc,
					}
				})
			end
			table.insert(rows, {example.input, example.output, example.categoryList})
		end
		return utilsLayout.table({
			hideEmptyColumns = true,
			headers = { s("header.input"), s("header.output"), s("header.categoriesAdded"), header = true },
			rows = rows
		})
	end
	
	local result = ""
	for _, example in ipairs(examples) do
		local headerStyles = {
			["width"] = "5rem" -- for alignment. See Template:Letter/Documentation for an example of why this is needed
		}
		result = result .. utilsLayout.table({
			hideEmptyRows = true,
			rows = {
				{
					{ header = true, content= s("header.input"), styles = headerStyles}, 
					example.input,
				},
				{
					{ header = true, content = s("header.output"), styles = headerStyles}, 
					example.output
				},
				{
					{ header = true, content = s("header.categoriesAdded"), styles = headerStyles },
					example.categoryList
				},
			}
		}) .. "\n"
	end
	return result
end

function h.syntax(templateName, params, repeatedGroup, format, indent)
	local args = {}
	for i, param in ipairs(params) do
		if param.param == VARARG_KEY then
			h.insertVarArgsSyntax(args, i, param.placeholder)
		elseif type(param.param) == "number" then
			args[i] = {
				param = param.param,
				value = param.placeholder or string.format("<%s>", param.name or param.param),
				inline = param.inline,
			}
		else
			args[i] = {
				param = param.param,
				inline = param.inline,
				value = ""
			}
		end
	end
	
	-- Add syntax for repeated group
	local afterArg = {}
	if #repeatedGroup > 0 then
		for i, n in ipairs({"1", "2", "N"}) do
			for _, param in ipairs(repeatedGroup) do
				table.insert(args, {
					param = param.param .. n,
					value = ""
				})
			end
			if i ~= 3 then
				afterArg[#args] = "\n"
			end
		end
	end
	
	local result = h.printTemplateInstance(templateName, args, format, {
		indent = indent,
		afterArg = afterArg,
	})
	return utilsMarkup.pre(result)
end
function h.insertVarArgsSyntax(args, i, placeholder)
	for j = 1, 3 do 
		table.insert(args, i+j-1, {
			param = j,
			value = placeholder .. j
		})
	end
	table.insert(args, i+3, {
		param = i+3,
		value = "..."
	})
	table.insert(args, i+4, {
		param = i+4,
		value = placeholder .. "N"
	})
end

function h.boilerplate(templateName, params, repeatedGroup, format, indent, options)
	local options = options or {}
	local args = {}
	local i = 1
	for _, param in ipairs(params) do
		if param.canOmit then
			-- do nothing
		elseif param.param == VARARG_KEY then
			h.insertVarArgBoilerplate(args, i)
		else
			i = i + 1
			table.insert(args,  {
				param = param.param,
				value = "",
				inline = param.inline,
			})
		end
	end
	local afterArg = {}
	for i = 1, options.repeatedGroupCount or 0 do
		for _, param in ipairs(repeatedGroup) do
			table.insert(args, {
				param = param.param .. i,
				value = "",
			})
		end
		if i ~= options.repeatedGroupCount then
			afterArg[#args] = "\n"
		end
	end
	local result = h.printTemplateInstance(templateName, args, format, {
		indent = indent,
		afterArg = afterArg
	})
	if options.list then
		local listArgs = {
			{
				param = 1,
				value = result,
			},
			{
				param = 2,
				value = result,
			},
			{
				param = 3,
				value = result,
			},
		}
		result = h.printTemplateInstance("List", listArgs, FORMATS.block, {
			indent = 1
		})
	end
	if options.before then
		result = options.before .. result
	end
	if options.after then
		result = result .. options.after
	end
	return utilsMarkup.pre(result)
end
function h.insertVarArgBoilerplate(args, i)
	for j = 1, 3 do 
		table.insert(args, i+j-1, {
			param = j,
			value = ""
		})
	end
end

function h.examplesFromFrame(examples)
	local result = {
		vertical = examples.vertical
	}
	for i, example in ipairs(examples) do
		local input = mw.text.unstripNoWiki(example)
		local output = mw.getCurrentFrame():preprocess(mw.text.decode(input))
		result[i] = {
			input = input,
			output = output
		}
	end
	return result
end

function h.examplesFromSpec(templateName, templateSpec)
	local paramOrder = templateSpec.paramOrder
	
	local repeatedParams = templateSpec.repeatedGroup and templateSpec.repeatedGroup.params or {}
	local repeatedParamsMap = utilsTable.invert(repeatedParams)
	local isRepeated = h.isRepeated(repeatedParamsMap)
	
	local examples = templateSpec.examples
	local result = {
		vertical = examples.vertical
	}
	for i, example in ipairs(examples) do
		local args = {}
		local unsortedArgs = {}
		local repeatedGroupArgs = {}
		for k, v in pairs(example.args or example) do
			local arg = {
				param = k,
				value = v,
				inline = templateSpec.params[k] and templateSpec.params[k].inline
			}
			
			local isRepeated, index, param = isRepeated(k)
			if isRepeated then
				repeatedGroupArgs[index] = repeatedGroupArgs[index] or {}
				utilsTable.merge(repeatedGroupArgs[index], {
					[param] = arg
				})
			else
				local idx = paramOrder and utilsTable.keyOf(paramOrder, k) 
				if idx then
					args[idx] = arg
				else
					table.insert(unsortedArgs, arg)
				end
			end
		end
		args = utilsTable.compact(args) -- needed when template has a mix of named parameters and optional anonymous parameters (e.g. Template:Cite)
		args = utilsTable.concat(args, unsortedArgs)
		
		-- repeated groups go last
		local afterArg = {}
		for i, group in ipairs(repeatedGroupArgs) do
			for _, param in ipairs(repeatedParams) do
				table.insert(args, group[param])
			end
			if i ~= #repeatedGroupArgs then
				afterArg[#args] = "\n"
			end
		end
		
		result[i] = {}
		result[i].input = h.printTemplateInstance(templateName, args, FORMATS[templateSpec.format], {
			indent = templateSpec.indent,
			afterArg = afterArg,
		})
		if not templateSpec.storesData then
			-- It's important not to use frame:expandTemplate here.
			-- We've had a bug in the past that we didn't catch because expandTemplate can accept args with "=" in them, while actual template calls cannot.
			result[i].output = mw.getCurrentFrame():preprocess(result[i].input)
		end
		result[i].desc = example.desc
	end
	return result
end
function h.isRepeated(repeatedParamsMap)
	-- @param param a template parameter e.g. "tab1"
	-- @return boolean indicating whether parameter is part of a repeated group
	-- @return number index of the repition, e.g. 1 for "tab1", 2 for "tab2", etc.
	-- @return name of the parameter without the index, e.g. "tab" for "tab1", "tab2", etc.
	return function(param)
		if type(param) == "number" then
			return false
		end
		local name = utilsString.trim(param, "0-9")
		local index = tonumber(string.sub(param, #name + 1))
		if not repeatedParamsMap[name] or not index then
			return false
		end
		return true, index, name
	end
end

function h.printTemplateInstance(name, args, format, options)
	local options = options or {}
	local afterArg = options.afterArg or {}
	local indent = string.rep(" ", options.indent or 0)
	local result = "{{" .. name
	for i, arg in ipairs(args) do
		local printedArg
		if type(arg.param) == "number" then
			printedArg = arg.value
		else
			printedArg = string.format("%s= %s", arg.param, arg.value)
		end
		if arg.inline then
			result = result .. FORMATS.inline.argSeparator .. printedArg
		else
			result = result .. string.format(format.argSeparator, indent) .. printedArg
		end
		if afterArg[i] then
			result = result .. afterArg[i]
		end
	end
	result = result .. format.afterLastArg .. "}}"
	return result
end

function h.params(params, positionalParamCount)
	local rows = {}
	for i, param in ipairs(params) do
		rows[i] = h.paramRow(param, positionalParamCount)
	end
	return utilsLayout.table({
		hideEmptyColumns = true,
		headers = {s("header.parameter"), s("header.status"), s("header.description"), s("header.enum")},
		rows = rows,
	})
end
function h.paramRow(param, i)
	if param.param == VARARG_KEY then
		local varArgs = utilsTable.map({"1", ".", ".", "N"}, utilsMarkup.code)
		varArgs = utilsMarkup.list(varArgs)
		paramCell = {{varArgs, utilsMarkup.code(param.name)}}
	elseif param.name and param.name ~= param.param then
		paramCell = {{utilsMarkup.code(param.param), utilsMarkup.code(param.name)}}
	else
		paramCell = utilsMarkup.code(param.param)
	end
	local statusCell = param.required and utilsMarkup.bold(s("status.required")) or s("status.optional")
	local descriptionCell = param.desc
	local enumCell = param.enum and h.printEnum(param) or ""
	return {paramCell, statusCell, descriptionCell, enumCell}
end
function h.printEnum(param)
	local enum = param.enum
	local dependsOn = param.enumDependsOn
	local enumReference = type(enum) == "table" and enum.reference or nil
	if dependsOn then
		return s("enum.depends", { arg = utilsMarkup.code(dependsOn) })
	elseif enumReference then
		return s("enum.ref", { dataSource = enumReference })
	else
		local values = utilsTable.map(enum, utilsMarkup.code)
		return utilsMarkup.bulletList(values)
	end
end

function h.templateData(templateSpec)
	local hasParamOrder = templateSpec.paramOrder
	local data = {
		description = templateSpec.purpose,
		params = {},
		paramOrder = templateSpec.paramOrder or {},
	}
	local repeatedGroupParams = templateSpec.repeatedGroup and templateSpec.repeatedGroup.params or {}
	local repeatedGroupCount = templateSpec.repeatedGroup and templateSpec.repeatedGroup.count or 0
	
	for k, v in pairs(templateSpec.params) do
		if not utilsTable.keyOf(repeatedGroupParams, k) then
			data.params[k] = {
				label = v.name or k,
				required = v.required,
				description = v.desc,
				aliases = type(k) == "number" and {v.name} or nil,
				type = v.type,
			}
			if not hasParamOrder then
				table.insert(data.paramOrder, k)
			end
		end
	end
	for _, param in ipairs(data.params) do
		if type(param.required) == "string" then
			param.required = true
		end
	end
	for i = 1, repeatedGroupCount do
		for _, paramName in ipairs(repeatedGroupParams) do
			local param = templateSpec.params[paramName]
			local paramKey = paramName .. i
			data.params[paramKey] = {
				label = l,
				required = param.required,
				description = param.desc,
				type = param.type
			}
			table.insert(data.paramOrder, paramKey)
		end
	end
	
	-- The following workaround is ugly, but necessary to ensure that data.params is encoded as a JSON object rather than an array.
	if utilsTable.isArray(templateSpec.params) then
		data.params["_"] = {
			label = "_",
			description = "Dummy parameter. Do not use.",
		}
		table.insert(data.paramOrder, "_")
	end
	
	local templateData = mw.getCurrentFrame():extensionTag("templatedata", mw.text.jsonEncode(data))
	local html = mw.html.create("div")
		:addClass("mw-collapsible mw-collapsed")
		:attr("data-expandtext", s("templateData.show"))
		:attr("data-collapsetext", s("templateData.hide"))
		:css("float", "left")
		:wikitext(templateData)
	return tostring(html)
end

function h.categories(templateSpec)
	local categories = {s("categories.all")}
	if templateSpec.usesData then
		table.insert(categories, s("categories.usesData"))
	end
	if templateSpec.storesData then
		table.insert(categories, s("categories.storesData"))
	end
	return utilsMarkup.categories(categories)
end

i18n.loadStrings({
	en = {
		categories = {
			all = "Category:Templates",
			usesData = "Category:Cargo Query Templates",
			storesData = "Category:Cargo Storage Templates",
		},
		usesData = "This template relies on the centralized data stored at ${dataSource}.",
		usesModuleData = "This template relies on configuration data stored at ${dataSource}.",
		storesData = "This template is used to store centralized data at ${dataSource}.",
		enum = {
			ref = "See ${dataSource}",
			depends = "Depends on ${arg}",
		},
		header = {
			description = "Description",
			parameter = "Parameter",
			status = "Status",
			enum = "Accepted values",
			categoriesAdded = "Categories added",
			input = "Input",
			output = "Output",
		},
		heading = {
			examples = "Examples",
			purpose = "Purpose",
			usage = "Usage",
		},
		status = {
			optional = "optional",
			required = "required",
		},
		tabs = {
			boilerplate = "Boilerplate",
			syntax = "Syntax",
		},
		templateData = {
			show = "show TemplateData ▼",
			hide = "hide TemplateData ▲",
		},
	}
})

p.Schemas = {
	TemplateSpec = {
		type = "record",
		properties = {
			{
				name = "purpose",
				type = "string",
				required = true,
				desc = "Purpose of the template.",
			},
			{
				name = "wip",
				type = "boolean",
				desc = "Flag to indicate the template is a work in progress.",
			},
			{
				name = "storesData",
				type = "string",
				desc = "For <code>/Store</code> templates, use this field to indicate which Data page this template is used on. If present, the template is added to [[:Category:Cargo Storage Templates]].",
			},
			{
				name = "usesData",
				oneOf = {
					{ 
						type = "string"
					},
					{
						type = "array",
						items = { type = "string" }
					},
				},
				desc = "For templates that use Cargo data, use this field to indicate which page(s) the data is stored on. If present, the template is added to [[:Category:Cargo Query Templates]].",
			},
			{
				name = "usesModuleData",
				type = "boolean",
				desc = "Set to true for templates that rely on [[:Category:Module Data|/Data]] pages in the Module namespace.",
			},
			{
				name = "format",
				type = "string",
				required = true,
				enum = {"inline", "block"},
				desc = "Indicates how the template should be laid out in source. <code>inline</code> for single-line templates, <code>block</code> for multiline templates."
			},
			{
				name = "params",
				type = "map",
				required = true,
				desc = "<p>Map of the template parameters. Numerical keys indicate positional parameters. String keys indicate named parameters. A special key named <code>" .. VARARG_KEY .. "</code> indicates a variadic parameter (i.e. a template with a variable number of trailing arguments, such as [[Template:List]]).</p><p>This data can be used by [[Module:UtilsArg]] to parse template arguments.</p>",
				keys = { 
					oneOf = {
						{ type = "string" },
						{ type = "number" },
					},
				},
				values = { _ref = "#/definitions/param" },
			},
			{
				name = "repeatedGroup",
				type = "record",
				desc = 'This is used to make templates that accept "arrays of objects" or "rows" of input. For an example, see [[Template:Trading Quest]]. (Ideally, [[Template:Tab2]] could be using this as well.)',
				properties = {
					{
						name = "name",
						required = true,
						type = "string",
						desc = "The key that the resulting array will be assigned to in the returned argument table.",
					},
					{
						name = "params",
						required = true,
						type = "array",
						items = { type = "string" },
						desc = "An array of parameter keys (similar to <code>paramOrder</code>), indicating which parameters are repeated."
					},
					{
						name = "count",
						required = true,
						type = "number",
						desc = "<p>Roughly the maximum amount of times one can expect the params to be repeated. This is merely to decide how many repetitions to include in the boilerplate. It is always possible to repeat more times than this number.</p><p>For example, [[Template:Trading Quest]] is set to 20. The longest trading quest in the series is 14 items, so 20 is likely to cover any future trading quests."
					}
				}
			},
			{
				name = "paramOrder",
				type = "array",
				items = {
					oneOf = {
						{ type = "number" },
						{ type = "string" },
					},
				},
				desc = "Array of parameter keys indicating what order they should be presented in.",
			},
			{
				name = "indent",
				type = "number",
				default = 0,
				desc = "Number of spaces to indent block-format parameters.",
			},
			{
				name = "boilerplate",
				type = "record",
				desc = "Directives for how generate boilerplate for the template.",
				properties = {
					{
						name = "before",
						type = "string",
						desc = "Any wikitext entered here will be prepended to the boilerplate",
					},
					{
						name = "after",
						type = "string",
						desc = "Any wikitext entered here will be appended to the boilerplate",
					},
					{
						name = "list",
						type = "boolean",
						desc = "If true, boilerplate includes [[Template:List]]. See [[Template:Game Rating]] for example.",
					},
				},
			},
			{
				name = "examples",
				desc = "Array of argument tables representing different invocations of the template + an optional <code>vertical</code> key. It is possible to add descriptions to specific examples as well. See [[Template:List]] for examples.",
				allOf = {
					{
						type = "record",
						properties = {
							{
								name = "vertical",
								type = "boolean",
								default = "false",
								desc = "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:Letter]])."
							},
						},
					},
					{
						type = "array",
						items = {
							type = "map",
							keys = {
								oneOf = {
									{ type = "number" },
									{ type = "string" },
								},
							},
							values = { type = "any" },
						},
					},
				},
			},
		},
		definitions = {
			param = {
				type = "record",
				properties = {
					{
						name = "name",
						type = "string",
						desc = "Use this to assign names to positional parameters.",
					},
					{
						name = "placeholder",
						type = "string",
						desc = "Placeholder to use for argument value when demonstrating template usage. Defaults to <code><name></code> for positional parameters; empty string for named parameters.",
					},
					{
						name = "type",
						type = "string",
						enum = {
							"unknown", 
							"number", 
							"string", 
							"line", 
							"boolean",
							"date",
							"url",
							"wiki-page-name",
							"wiki-file-name",
							"wiki-template-name",
							"wiki-user-name",
							"content",
							"unbalanced-wikitext",
							reference = "{{Mediawiki|Extension:TemplateData}}",
						},
						desc = "One of the {{Mediawiki|Extension:TemplateData}} types."
					},
					{
						name = "required",
						oneOf = {
							{ type = "boolean" },
							{ type = "string" },
						},
						desc = "Indicates a required parameter.",
					},
					{
						name = "deprecated",
						type = "boolean",
						desc = "Indicates a parameter that should no longer be used.",
					},
					{
						name = "enum",
						type = "any",
						desc = "An array of allowed values. Optionally, a <code>reference</code> key can be added to the Lua table which links to a page listing all the allowed values (see [[Module:Franchise#enum]] for example).",
					},
					{
						name = "enumDependsOn",
						type = "string",
						desc = "If the allowed values for this parameter depends on the value of another parameter, specify that parameter name here. Then, instead of <code>enum</code> 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:Letter]] for an example.",
					},
					{
						name = "desc",
						type = "string",
						required = true,
						desc = "Wikitext description of the parameter.",
					},
					{
						name = "canOmit",
						type = "boolean",
						desc = "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.",
					},
					{
						name = "inline",
						type = "boolean",
						desc = 'If true, then the parameter will be printed on the same line as the previous parameter, even if <code>format</code> is set to <code>"block"</code>. See [[Template:Sequence/Store]] for example.',
					},
					{
						name = "trim",
						type = "boolean",
						desc = "Indicator to [[Module:UtilsArg#parse|utilsArg.parse]] that this template argument should be trimmed using [[Module:UtilsString#trim|utilsString.trim]].",
					},
					{
						name = "nilIfEmpty",
						type = "boolean",
						desc = "Indicator to [[Module:UtilsArg#parse|utilsArg.parse]] that this template argument should be made nil if it is an empty string, using [[Module:UtilsString#nilIfEmpty|utilsString.nilIfEmpty]].",
					},
					{
						name = "split",
						oneOf = {
							{ type = "string" },
							{ type = "boolean" },
						},
						desc = "Indicator to [[Module:UtilsArg#parse|utilsArg.parse]]. If set to <code>true</code>, the template argument is treated as a list of commma-separated values, to be turned into an array using [[Module:UtilsString#split|utilsString.split]]. If set to a string, that string will be used as the splitting pattern.",
					},
				},
			},
		},
	},
}

return p