Jump to content
Toggle menu
  • 36 articles
  • 9 files
  • 1 users
  • 417 edits
Paracosm
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Documentation: Difference between revisions

From Paracosm
mNo edit summary
m update from wiki-tools
Line 1: Line 1:
require('strict')
--- @module Documentation
--- The documentation header on module and template pages and on their /doc
--- subpages, followed by the Technical details panel. Template:Documentation
--- invokes doc() at the top of each /doc page.
-- <nowiki>
-- <nowiki>
local dependencyList = require( 'Module:DependencyList' )
local button = require('Module:ButtonLua')
local hatnote = require( 'Module:Hatnote' )._hatnote
local hatnote = require('Module:Hatnote')._hatnote
local mbox = require( 'Module:Mbox' )._mbox
local icon = require('Module:Icon')
local i18n = require( 'Module:i18n' ):new()
local yesno = require('Module:Yesno')
local TNT = require( 'Module:Translate' ):new()
 
local lang = mw.getContentLanguage()
local p = {}
local p = {}


local NS_TEMPLATE = 10
local NS_MODULE = 828
local REPO_URL = 'https://github.com/StarCitizenTools/wiki-tools/tree/main/pages/'
local WIKIPEDIA_URL = 'https://en.wikipedia.org/wiki/'
local ICONS = {
page = 'CdxIconBook.svg',
edit = 'CdxIconEdit.svg',
history = 'CdxIconHistory.svg',
purge = 'CdxIconReload.svg',
back = 'CdxIconArrowPrevious.svg',
}


--- Wrapper function for Module:i18n.translate
--- @param value string|nil a {{Documentation}} flag such as `git=`
---
--- @return boolean
--- @param key string The translation key
local function isOn(value)
--- @return string If the key was not found, the key is returned
return yesno(value) == true
local function t( key )
return i18n:translate( key )
end
end


--- The visible text of a wikitext link, for the panel's plain-text facts.
--- @param wikitext string
--- @return string
local function linkText(wikitext)
return wikitext:match('^%s*%[%[[^|%]]*|(.-)%]%]%s*$')
or wikitext:match('^%s*%[%[(.-)%]%]%s*$')
or wikitext:match('^%s*%[%S+%s+(.-)%]%s*$')
or wikitext
end


--- FIXME: This should go to somewhere else, like Module:Common
--- Where the page was imported from, as a wikitext link: `importedFrom=`, or
--- Calls TNT with the given key
--- its `fromWikipedia=` shorthand for the same title on the English Wikipedia.
---
--- @param args table the {{Documentation}} arguments
--- @param key string The translation key
--- @param page string the documented page
--- @return string If the key was not found in the .tab page, the key is returned
--- @return string|nil
local function translate( key, ... )
local function importOrigin(args, page)
local success, translation = pcall( TNT.format, 'Module:Documentation/i18n.json', key or '', ... )
if args.importedFrom and args.importedFrom ~= '' then
return args.importedFrom
end
if isOn(args.fromWikipedia) then
return '[' .. WIKIPEDIA_URL .. mw.uri.encode(page, 'WIKI') .. ' Wikipedia]'
end
return nil
end


if not success or translation == nil then
--- The Source row lines of the Technical details panel: where the page is
return key
--- maintained (`git=`) and where it came from.
--- @param args table the {{Documentation}} arguments
--- @param kind string 'module' or 'template'
--- @param rootText string
--- @param page string the documented page
--- @return table[] DependenciesSource[]
local function sources(args, kind, rootText, page)
local list = {}
if isOn(args.git) then
local url = REPO_URL .. kind .. '/' .. mw.uri.encode(rootText, 'PATH')
list[#list + 1] = { label = 'wiki-tools', text = 'Synced with [' .. url .. ' wiki-tools] on GitHub' }
end
end
local origin = importOrigin(args, page)
if origin then
-- The label goes in the panel's <summary>, which must hold no link: a bare URL would autolink.
list[#list + 1] = { label = 'from ' .. mw.text.nowiki(linkText(origin)), text = 'Imported from ' .. origin }
end
return list
end


return translation
--- @param kind string 'module' or 'template'
--- @param onDoc boolean the render is the /doc subpage itself
--- @param baseText string the /doc page's base page name, its sort key
--- @param imported boolean
--- @return string
local function categories(kind, onDoc, baseText, imported)
local plural = kind == 'module' and 'Modules' or 'Templates'
if onDoc then
return '[[Category:' .. plural .. ' documentation|' .. baseText .. ']]'
end
local out = {}
if kind == 'module' then
out[#out + 1] = '[[Category:Modules]]'
end
if imported then
out[#out + 1] = '[[Category:Imported ' .. plural:lower() .. ']]'
end
return table.concat(out)
end
end


--- A quiet icon-only header button. ButtonLua names it with aria-label; the
--- wrapper is what the phone layout stretches.
--- @param props table ButtonProps: label, icon and link
--- @return string
local function action(props)
props.weight = 'quiet'
props.iconOnly = true
return tostring(mw.html.create('span'):addClass('t-documentation__action'):wikitext(button.render(props)))
end


function p.doc( frame )
--- @param title string
    local title = mw.title.getCurrentTitle()
--- @param subtitle string wikitext
    local args = frame:getParent().args
--- @param actions string[]
    local page = args[1] or string.gsub( title.fullText, '/[Dd]o[ck]u?$', '' )
--- @return string
    local ret, cats, ret1, ret2, ret3
local function header(title, subtitle, actions)
    local pageType = title.namespace == 828 and 'module' or 'template'
local root = mw.html.create('div'):addClass('t-documentation__header')
root:tag('span')
:addClass('t-documentation__icon')
:wikitext(icon.render({ icon = ICONS.page, mask = true, size = '1.125rem' }))
local heading = root:tag('div'):addClass('t-documentation__heading')
heading
:tag('div')
:addClass('t-documentation__title')
:attr('role', 'heading')
:attr('aria-level', '2')
:wikitext(title)
heading:tag('div'):addClass('t-documentation__subtitle'):wikitext(subtitle)
root:tag('div'):addClass('t-documentation__actions'):wikitext(table.concat(actions))
return tostring(root)
end


    -- subpage header
--- A failure in Module:Dependencies, or a module it requires, must not break
    if title.subpageText == 'doc' then
--- every documentation page: require and render it inside a pcall.
ret = mbox(
--- @param options table DependenciesPanelOptions
translate( 'message_subpage_title', page ),
--- @return string
translate( 'message_subpage_desc', page, translate( pageType ) ),
local function technicalDetails(options)
{ icon = 'Codex icon notice.svg' }
local ok, result = pcall(function()
    )
return require('Module:Dependencies').panel(options)
end)
if ok then
return result
end
return '<strong class="error">' .. mw.text.nowiki(tostring(result)) .. '</strong>'
end


        if title.namespace == 10 then -- Template namespace
--- `{{#invoke:Documentation|doc}}`, from Template:Documentation.
            cats = '[[Category:' .. string.format( t( 'category_documentation' ), t( 'category_' .. pageType ) ) .. '|' .. title.baseText .. ']]'
--- @param frame table
            ret2 = dependencyList._main()
--- @return string
        elseif title.namespace == 828 then -- Module namespace
function p.doc(frame)
            cats = '[[Category:' .. string.format( t( 'category_documentation' ), t( 'category_' .. pageType ) ) .. '|' .. title.baseText .. ']]'
local title = mw.title.getCurrentTitle()
            ret2 = dependencyList._main()
local args = frame:getParent().args
            ret2 = ret2 .. require('Module:Module toc').main()
local page = args[1]
        else
if page == nil or page == '' then
            cats = ''
page = title.fullText:gsub('/[Dd]o[ck]u?$', '')
            ret2 = ''
end
        end
local kind = title.namespace == NS_MODULE and 'module' or 'template'
local onDoc = title.subpageText == 'doc'


        return tostring( ret ) .. ret2 .. cats
-- Left open on purpose: {{Documentation}} sits at the top of the /doc page,
    end
-- and the wrapper holds the rest of it until the parser closes it at the end.
local out = { '<div class="t-documentation' .. (isOn(args.git) and ' t-documentation--git' or '') .. '">' }
out[#out + 1] = frame:extensionTag({ name = 'templatestyles', args = { src = 'Module:Icon/styles.css' } })
out[#out + 1] = frame:extensionTag({ name = 'templatestyles', args = { src = 'Module:Documentation/styles.css' } })


    -- template header
if onDoc then
    -- don't use mw.html as we aren't closing the main div tag
out[#out + 1] = header('Documentation subpage', 'Shown on [[' .. page .. ']]', {
    ret1 = '<div class="documentation">'
action({ label = 'Back to the ' .. kind, icon = ICONS.back, link = page }),
})
else
local doc = page .. '/doc'
out[#out + 1] =
header(kind == 'module' and 'Module documentation' or 'Template documentation', 'From [[' .. doc .. ']]', {
action({ label = 'Edit', icon = ICONS.edit, link = 'Special:EditPage/' .. doc }),
action({ label = 'History', icon = ICONS.history, link = 'Special:PageHistory/' .. doc }),
action({ label = 'Purge', icon = ICONS.purge, link = 'Special:Purge/' .. title.fullText }),
})
end


    ret2 = mw.html.create( nil )
local notices = mw.html.create('div'):addClass('t-documentation__notices')
        :tag( 'div' )
if not onDoc and title.namespace == NS_MODULE and title.subpageText == 'testcases' then
            :addClass( 'documentation-header' )
notices:wikitext(
            :tag( 'span' )
hatnote(
                :addClass( 'documentation-title' )
'This is the test cases page for the module [[Module:' .. title.baseText .. ']].',
                :wikitext( lang:ucfirst( translate('message_documentation_title', pageType ) ) )
{ icon = 'WikimediaUI-LabFlask.svg' }
                :done()
            :tag( 'span' )
                :addClass( 'documentation-links plainlinks' )
                :wikitext(
                    '[[' .. tostring( mw.uri.fullUrl( page .. '/doc', {action='view'} ) ) .. ' view]]' ..
                    '[[' .. tostring( mw.uri.fullUrl( page .. '/doc', {action='edit'} ) ) .. ' edit]]' ..
                    '[[' .. tostring( mw.uri.fullUrl( page .. '/doc', {action='history'} ) ) .. ' history]]' ..
                    '[<span class="jsPurgeLink">[' .. tostring( mw.uri.fullUrl( title.fullText, { action = 'purge' } ) ) .. ' purge]</span>]'
                )
                :done()
            :done()
        :tag( 'div' )
            :addClass( 'documentation-subheader' )
            :tag( 'span' )
                :addClass( 'documentation-documentation' )
                :wikitext( translate( 'message_transclude_desc', page ) )
                :done()
            :wikitext( frame:extensionTag{ name = 'templatestyles', args = { src = 'Module:Documentation/styles.css'} } )
            :done()
 
    ret3 = {}
 
    if args.git then
    local repoUrl = 'https://github.com/StarCitizenTools/wiki-tools/tree/main/pages/'
    .. pageType .. '/' .. mw.uri.encode( title.rootText, 'PATH' )
 
    table.insert( ret3,
    mbox(
    "'''" .. page .. "''' is maintained in the [" .. repoUrl .. " wiki-tools] repository on GitHub.",
    'This ' .. pageType .. ' is synced from the Git repository. Any on-wiki changes should also be made in GitHub, otherwise they may be overwritten on the next deployment.',
    { icon = 'Codex icon code.svg' }
    )
    )
    end
 
    if args.fromWikipedia then
    table.insert( ret3,
    mbox(
    translate(
'message_from_wikipedia',
title.fullText,
mw.uri.encode( page, 'WIKI' ),
page
),
translate(
'message_from_wikipedia_subtext',
pageType
),
{ icon = 'Codex icon logo-Wikipedia.svg' }
)
)
  )
)
  --- Set category
end
  table.insert( ret3, '[[Category:' .. string.format( t( 'category_imported_from_wikipedia' ), lang:ucfirst( pageType ) ) .. ']]' )
notices:wikitext(technicalDetails({
    end
addCategories = (not onDoc) and args.category or nil,
sources = sources(args, kind, title.rootText, page),
}))
out[#out + 1] = tostring(notices)


    if title.namespace == 828 then
if title.namespace == NS_TEMPLATE or title.namespace == NS_MODULE then
    -- Has config
out[#out + 1] = categories(kind, onDoc, title.baseText, importOrigin(args, page) ~= nil)
    if mw.title.new( title.fullText .. '/config.json', 'Module' ).exists then
end
table.insert( ret3,
mbox(
    translate(
    'message_module_configuration',
    title.fullText,
    title.fullText
    ),
    translate( 'message_module_configuration_subtext' ),
    { icon = 'Codex icon settings.svg' }
    )
)
    end


    -- Has localization
if not onDoc and title.namespace == NS_MODULE then
    if mw.title.new( title.fullText .. '/i18n.json', 'Module' ).exists then
local testcases = title.text .. '/testcases'
table.insert( ret3,
if mw.title.new(testcases, 'Module').exists then
mbox(
out[#out + 1] = frame:preprocess('{{#invoke:' .. testcases .. '|run}}')
    translate(
    'message_module_i18n',
    title.fullText,
    title.fullText
    ),
    translate( 'message_module_i18n_subtext' ),
    { icon = 'Codex icon language.svg' }
    )
)
    end
 
    -- Testcase page
    if title.subpageText == 'testcases' then
    table.insert( ret3,
    hatnote(
    translate( 'message_module_tests', title.baseText ),
    { icon = 'Codex icon labFlask.svg' }
    )
    )
end
end
table.insert( ret3, string.format( '[[Category:%s]]', t( 'category_module' ) ) )
    end
    --- Dependency list
    table.insert( ret3, dependencyList._main( nil, args.category, args.isUsed ) )
    -- Has templatestyles
if mw.title.new( title.fullText .. '/styles.css' ).exists then
table.insert( ret3,
hatnote(
    translate( 'message_styles', title.fullText, title.fullText ),
    { icon = 'Codex icon palette.svg' }
    )
)
end
end


    --- Module stats bar
return table.concat(out)
    if title.namespace == 828 then
end
-- Function list
table.insert( ret3, require( 'Module:Module toc' ).main() )
 
-- Unit tests
local testcaseTitle = title.text .. '/testcases'
if mw.title.new( testcaseTitle, 'Module' ).exists then
-- There is probably a better way :P
table.insert( ret3, frame:preprocess( '{{#invoke:' .. testcaseTitle .. '|run}}' ) )
    end
    end


    return ret1 .. tostring( ret2 ) .. '<div class="documentation-content">' .. table.concat( ret3 ) .. '</div>'
-- Test-only exports. Not part of the public API.
end
p._internal = {
categories = categories,
linkText = linkText,
sources = sources,
}


return p
return p
-- </nowiki>
-- </nowiki>

Revision as of 00:46, 12 November 2026

Documentation for this module may be created at Module:Documentation/doc

require('strict')

--- @module Documentation
--- The documentation header on module and template pages and on their /doc
--- subpages, followed by the Technical details panel. Template:Documentation
--- invokes doc() at the top of each /doc page.

-- <nowiki>
local button = require('Module:ButtonLua')
local hatnote = require('Module:Hatnote')._hatnote
local icon = require('Module:Icon')
local yesno = require('Module:Yesno')

local p = {}

local NS_TEMPLATE = 10
local NS_MODULE = 828
local REPO_URL = 'https://github.com/StarCitizenTools/wiki-tools/tree/main/pages/'
local WIKIPEDIA_URL = 'https://en.wikipedia.org/wiki/'
local ICONS = {
	page = 'CdxIconBook.svg',
	edit = 'CdxIconEdit.svg',
	history = 'CdxIconHistory.svg',
	purge = 'CdxIconReload.svg',
	back = 'CdxIconArrowPrevious.svg',
}

--- @param value string|nil a {{Documentation}} flag such as `git=`
--- @return boolean
local function isOn(value)
	return yesno(value) == true
end

--- The visible text of a wikitext link, for the panel's plain-text facts.
--- @param wikitext string
--- @return string
local function linkText(wikitext)
	return wikitext:match('^%s*%[%[[^|%]]*|(.-)%]%]%s*$')
		or wikitext:match('^%s*%[%[(.-)%]%]%s*$')
		or wikitext:match('^%s*%[%S+%s+(.-)%]%s*$')
		or wikitext
end

--- Where the page was imported from, as a wikitext link: `importedFrom=`, or
--- its `fromWikipedia=` shorthand for the same title on the English Wikipedia.
--- @param args table the {{Documentation}} arguments
--- @param page string the documented page
--- @return string|nil
local function importOrigin(args, page)
	if args.importedFrom and args.importedFrom ~= '' then
		return args.importedFrom
	end
	if isOn(args.fromWikipedia) then
		return '[' .. WIKIPEDIA_URL .. mw.uri.encode(page, 'WIKI') .. ' Wikipedia]'
	end
	return nil
end

--- The Source row lines of the Technical details panel: where the page is
--- maintained (`git=`) and where it came from.
--- @param args table the {{Documentation}} arguments
--- @param kind string 'module' or 'template'
--- @param rootText string
--- @param page string the documented page
--- @return table[] DependenciesSource[]
local function sources(args, kind, rootText, page)
	local list = {}
	if isOn(args.git) then
		local url = REPO_URL .. kind .. '/' .. mw.uri.encode(rootText, 'PATH')
		list[#list + 1] = { label = 'wiki-tools', text = 'Synced with [' .. url .. ' wiki-tools] on GitHub' }
	end
	local origin = importOrigin(args, page)
	if origin then
		-- The label goes in the panel's <summary>, which must hold no link: a bare URL would autolink.
		list[#list + 1] = { label = 'from ' .. mw.text.nowiki(linkText(origin)), text = 'Imported from ' .. origin }
	end
	return list
end

--- @param kind string 'module' or 'template'
--- @param onDoc boolean the render is the /doc subpage itself
--- @param baseText string the /doc page's base page name, its sort key
--- @param imported boolean
--- @return string
local function categories(kind, onDoc, baseText, imported)
	local plural = kind == 'module' and 'Modules' or 'Templates'
	if onDoc then
		return '[[Category:' .. plural .. ' documentation|' .. baseText .. ']]'
	end
	local out = {}
	if kind == 'module' then
		out[#out + 1] = '[[Category:Modules]]'
	end
	if imported then
		out[#out + 1] = '[[Category:Imported ' .. plural:lower() .. ']]'
	end
	return table.concat(out)
end

--- A quiet icon-only header button. ButtonLua names it with aria-label; the
--- wrapper is what the phone layout stretches.
--- @param props table ButtonProps: label, icon and link
--- @return string
local function action(props)
	props.weight = 'quiet'
	props.iconOnly = true
	return tostring(mw.html.create('span'):addClass('t-documentation__action'):wikitext(button.render(props)))
end

--- @param title string
--- @param subtitle string wikitext
--- @param actions string[]
--- @return string
local function header(title, subtitle, actions)
	local root = mw.html.create('div'):addClass('t-documentation__header')
	root:tag('span')
		:addClass('t-documentation__icon')
		:wikitext(icon.render({ icon = ICONS.page, mask = true, size = '1.125rem' }))
	local heading = root:tag('div'):addClass('t-documentation__heading')
	heading
		:tag('div')
		:addClass('t-documentation__title')
		:attr('role', 'heading')
		:attr('aria-level', '2')
		:wikitext(title)
	heading:tag('div'):addClass('t-documentation__subtitle'):wikitext(subtitle)
	root:tag('div'):addClass('t-documentation__actions'):wikitext(table.concat(actions))
	return tostring(root)
end

--- A failure in Module:Dependencies, or a module it requires, must not break
--- every documentation page: require and render it inside a pcall.
--- @param options table DependenciesPanelOptions
--- @return string
local function technicalDetails(options)
	local ok, result = pcall(function()
		return require('Module:Dependencies').panel(options)
	end)
	if ok then
		return result
	end
	return '<strong class="error">' .. mw.text.nowiki(tostring(result)) .. '</strong>'
end

--- `{{#invoke:Documentation|doc}}`, from Template:Documentation.
--- @param frame table
--- @return string
function p.doc(frame)
	local title = mw.title.getCurrentTitle()
	local args = frame:getParent().args
	local page = args[1]
	if page == nil or page == '' then
		page = title.fullText:gsub('/[Dd]o[ck]u?$', '')
	end
	local kind = title.namespace == NS_MODULE and 'module' or 'template'
	local onDoc = title.subpageText == 'doc'

	-- Left open on purpose: {{Documentation}} sits at the top of the /doc page,
	-- and the wrapper holds the rest of it until the parser closes it at the end.
	local out = { '<div class="t-documentation' .. (isOn(args.git) and ' t-documentation--git' or '') .. '">' }
	out[#out + 1] = frame:extensionTag({ name = 'templatestyles', args = { src = 'Module:Icon/styles.css' } })
	out[#out + 1] = frame:extensionTag({ name = 'templatestyles', args = { src = 'Module:Documentation/styles.css' } })

	if onDoc then
		out[#out + 1] = header('Documentation subpage', 'Shown on [[' .. page .. ']]', {
			action({ label = 'Back to the ' .. kind, icon = ICONS.back, link = page }),
		})
	else
		local doc = page .. '/doc'
		out[#out + 1] =
			header(kind == 'module' and 'Module documentation' or 'Template documentation', 'From [[' .. doc .. ']]', {
				action({ label = 'Edit', icon = ICONS.edit, link = 'Special:EditPage/' .. doc }),
				action({ label = 'History', icon = ICONS.history, link = 'Special:PageHistory/' .. doc }),
				action({ label = 'Purge', icon = ICONS.purge, link = 'Special:Purge/' .. title.fullText }),
			})
	end

	local notices = mw.html.create('div'):addClass('t-documentation__notices')
	if not onDoc and title.namespace == NS_MODULE and title.subpageText == 'testcases' then
		notices:wikitext(
			hatnote(
				'This is the test cases page for the module [[Module:' .. title.baseText .. ']].',
				{ icon = 'WikimediaUI-LabFlask.svg' }
			)
		)
	end
	notices:wikitext(technicalDetails({
		addCategories = (not onDoc) and args.category or nil,
		sources = sources(args, kind, title.rootText, page),
	}))
	out[#out + 1] = tostring(notices)

	if title.namespace == NS_TEMPLATE or title.namespace == NS_MODULE then
		out[#out + 1] = categories(kind, onDoc, title.baseText, importOrigin(args, page) ~= nil)
	end

	if not onDoc and title.namespace == NS_MODULE then
		local testcases = title.text .. '/testcases'
		if mw.title.new(testcases, 'Module').exists then
			out[#out + 1] = frame:preprocess('{{#invoke:' .. testcases .. '|run}}')
		end
	end

	return table.concat(out)
end

-- Test-only exports. Not part of the public API.
p._internal = {
	categories = categories,
	linkText = linkText,
	sources = sources,
}

return p
-- </nowiki>
Last modified