This documentation is transcluded from Module:DependencyList/doc. Changes can be proposed in the talk page.
Module:DependencyList's function main is invoked by Template:DependencyList.
Module:DependencyList requires Module:DPLlua.
Module:DependencyList requires Module:Enum.
Module:DependencyList requires Module:LibraryUtil.
Module:DependencyList requires Module:Paramtest.
Module:DependencyList requires Module:Yesno.
Module:DependencyList is required by Module:Documentation.
This template is meant to be used on module documentations.
{{DependencyList|<<Module name>>}}
All parameters are optional. If Module name is omitted the page name will be used instead; /doc is automatically removed.
If category is false then no categories will be added to the page, the default value is true.
If compact is true links will be separated by commas instead of every link on a new line.
Examples
{{DependencyList|<Module:For>}}
This module is unused.This module is neither invoked by a template nor required/loaded by another module. If this is in error, make sure to add
{{Documentation}}/{{No documentation}} to the calling template's or parent's module documentation.Module:For requires Module:Arguments.
Module:For requires Module:Hatnote.
Module:For requires Module:Hatnote list.
Module:For requires Module:Yesno.
{{DependencyList|<Module:Infobox>}}
Module:Infobox's function infobox is invoked by Template:Infobox.
-- <nowiki>
local p = {}
local libraryUtil = require( 'libraryUtil' )
local enum = require( 'Module:Enum' )
local yn = require( 'Module:Yesno' )
local param = require( 'Module:Paramtest' )
local dpl = require( 'Module:DPLlua' )
local moduleIsUsed = false
local MAX_DYNAMIC_REQUIRE_LIST_LENGTH = 30
local dynamicRequireListQueryCache = {}
--- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'.
---@param content string @The content of the module to look in as a string
---@param varName string
---@return string
local function substVarValue( content, varName )
if varName:find( "%b''" ) or varName:find( '%b""' ) then -- Look for balanced quotes
return varName -- Is already a string
else
local res = content:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or content:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or ''
if res:find( '^(["\'])[Mm]odule:[%S]+%1' ) and not res:find( '%.%.' ) then
return mw.text.trim( res )
else
return ''
end
end
end
--- Used in case a construct like 'require( "Module:wowee/" .. isTheBest )' is found.
--- Will return a list of pages which satisfy this pattern where 'isTheBest' can take any value.
---@param query string
---@return string[] @Sequence of strings
local function getDynamicRequireList( query )
query = mw.text.split( query, '..', true )
query = enum.map( query, function(x) return mw.text.trim(x) end )
query = enum.map( query, function(x) return (x:match('^[\'\"]([^\'\"]+)[\'\"]$') or '%') end )
query = table.concat( query )
query = query:gsub( '^[Mm]odule:', '' )
if dynamicRequireListQueryCache[ query ] then
return dynamicRequireListQueryCache[ query ]
end
local list = dpl.ask{
namespace = 'Module',
titlematch = query,
nottitlematch = '%/doc|%sandbox%%|'..query..'/%',
distinct = 'strict',
ignorecase = true,
ordermethod = 'title',
count = MAX_DYNAMIC_REQUIRE_LIST_LENGTH + 1,
skipthispage = 'no',
allowcachedresults = true,
cacheperiod = 604800 -- One week
}
if #list > MAX_DYNAMIC_REQUIRE_LIST_LENGTH then
list = { 'Module:' .. query }
end
dynamicRequireListQueryCache[ query ] = list
return list
end
--- Returns a list of modules loaded and required by module 'moduleName'.
---@param moduleName string
---@return string[], string[]
local function getRequireList( moduleName )
local content = mw.title.new( moduleName ):getContent()
local requireList = {}
local loadDataList = {}
local usedTemplateList = {}
local dynamicRequirelist = {}
local dynamicLoadDataList = {}
assert( param.has_content( content ), string.format( '%s does not exist', moduleName ) )
content = content:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments
local function dualGmatch( str, pat1, pat2 )
local f1 = string.gmatch( str, pat1 )
local f2 = string.gmatch( str, pat2 )
return function()
return f1() or f2()
end
end
for match in dualGmatch( content, 'require%s*%(([^%)]+)', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do
match = mw.text.trim( match )
match = substVarValue( content, match )
if match:find( '%.%.' ) then
for _, x in ipairs( getDynamicRequireList( match ) ) do
table.insert( dynamicRequirelist, x )
end
elseif match ~= '' then
match = match:gsub( '[\"\']', '' ):gsub( '_', ' ' )
if match == 'libraryUtil' then
match = 'Module:LibraryUtil'
end
table.insert( requireList, match )
end
end
for match in dualGmatch( content, 'mw%.loadData%s*%(([^%)]+)', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)' ) do
match = mw.text.trim( match )
match = substVarValue( content, match )
if match:find( '%.%.' ) then
for _, x in ipairs( getDynamicRequireList( match ) ) do
table.insert( dynamicLoadDataList, x )
end
elseif match ~= '' then
match = match:gsub( '[\"\']', '' ):gsub( '_', ' ' )
table.insert( loadDataList, match )
end
end
for func, match in string.gmatch( content, 'pcall%s*%(([^,]+),([^%),]+)' ) do
func = mw.text.trim( func )
match = mw.text.trim( match )
if func == 'require' then
for _, x in ipairs( getDynamicRequireList( match ) ) do
table.insert( dynamicRequirelist, x )
end
elseif func == 'mw.loadData' then
for _, x in ipairs( getDynamicRequireList( match ) ) do
table.insert( dynamicLoadDataList, x )
end
end
end
for preprocess in string.gmatch( content, ':preprocess%s*%((.-)%)' ) do
for template in string.gmatch( preprocess, '{{(.-)[|}]' ) do
if template ~= '' then
if template:find( ':' ) then
table.insert( usedTemplateList, template )
else
table.insert( usedTemplateList, 'Template:' .. template )
end
end
end
end
dynamicRequirelist = enum.reject( dynamicRequirelist, function(x) return (enum.contains(requireList, x) or enum.contains(loadDataList, x)) end )
dynamicLoadDataList = enum.reject( dynamicLoadDataList, function(x) return (enum.contains(requireList, x) or enum.contains(loadDataList, x)) end )
requireList = enum.insert( requireList, dynamicRequirelist )
requireList = enum.unique( requireList )
loadDataList = enum.insert( loadDataList, dynamicLoadDataList )
loadDataList = enum.unique( loadDataList )
usedTemplateList = enum.unique( usedTemplateList )
table.sort( requireList )
table.sort( loadDataList )
table.sort( usedTemplateList )
return requireList, loadDataList, usedTemplateList
end
--- Makes the first letter of the input string upper case
---@param str string
---@return string
local function ucfirst( str )
return str:gsub( '^.', function(c) return c:upper() end )
end
--- Returns a list with module and function names used in all '{{#Invoke:moduleName|funcName}}' found on page 'templateName'.
---@param templateName string
---@return table<string, string>[]
local function getInvokeCallList( templateName )
local content = mw.title.new( templateName ):getContent()
local invokeList = {}
assert( param.has_content( content ), string.format( '%s does not exist', templateName ) )
for moduleName, funcName in string.gmatch( content, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
moduleName = ucfirst( moduleName )
moduleName = string.format( 'Module:%s', moduleName )
moduleName = moduleName:gsub( '_', ' ' )
table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
end
invokeList = enum.unique( invokeList, function(x) return x.moduleName..x.funcName end )
table.sort( invokeList, function(x, y) return x.moduleName..x.funcName < y.moduleName..y.funcName end )
return invokeList
end
---@param pageName string
---@param addCategories boolean
---@return string
local function messageBoxUnused( pageName, addCategories )
local html = mw.html.create( 'div' ):addClass( 'mbox mbox-med mbox-unusedmodule' ):attr( 'role', 'presentation')
html:tag( 'span' )
:addClass( 'mbox-title' )
:tag( 'span' )
:addClass( 'mbox-icon metadata' )
:wikitext( '[[File:WikimediaUI-Alert.svg|14px|link=]]' )
:done()
:wikitext( 'This module is unused.' )
:done()
:tag( 'span' )
:addClass( 'mbox-text' )
:wikitext(
string.format( 'This module is neither invoked by a template nor required/loaded by another module. If this is in error, make sure to add <code>{{[[Template:Documentation|Documentation]]}}</code>/<code>{{[[Template:No documentation|No documentation]]}}</code> to the calling template\'s or parent\'s module documentation.',
pageName
)
)
:wikitext( addCategories and '[[Category:Unused modules]]' or '' )
:done()
:done()
return tostring( html )
end
--- Creates a link to [[Special:Search]] showing all pages found by getDynamicRequireList() in case it found more than MAX_DYNAMIC_REQUIRE_LIST_LENGTH pages.
---@param query string @This will be in a format like 'Module:Wowee/%' or 'Module:Wowee/%/data'
---@return string
local function formatDynamicQueryLink( query )
local prefix = query:match( '^([^/]+)' )
local linkText = query:gsub( '%%', '< ... >' )
query = query:gsub( '^Module:', '' )
query = query:gsub( '([^/]+)/?', function ( match )
if match == '%' then
return '\\/[^\\/]+'
else
return '\\/"' .. match .. '"'
end
end )
query = query:gsub( '^\\/', '' )
query = string.format(
'intitle:/%s%s/i -intitle:/%s\\/""/i -intitle:doc prefix:"%s"',
query,
query:find( '"$' ) and '' or '""',
query,
prefix
)
return string.format( '<span class="plainlinks">[%s %s]</span>', tostring( mw.uri.fullUrl( 'Special:Search', { search = query } ) ), linkText )
end
---@param templateName string
---@param addCategories boolean
---@param invokeList table<string, string>[] @This is the list returned by getInvokeCallList()
---@return string
local function formatInvokeCallList( templateName, addCategories, invokeList )
local category = addCategories and '[[Category:Lua-based templates]]' or ''
local res = {}
for _, item in ipairs( invokeList ) do
table.insert( res, string.format(
"<div role='note' class='hatnote'><div class=hatnote-container navigation-not-searchable'><span class='hatnote-icon metadata'>[[File:WikimediaUI-Code.svg|14px|link=]]</span>'''%s''' invokes function '''%s''' in [[%s]] using [[Star Citizen:Lua|Lua]].</div></div>",
templateName,
item.funcName,
item.moduleName
) )
end
if #invokeList > 0 then
table.insert( res, category )
end
return table.concat( res )
end
---@param moduleName string
---@param addCategories boolean
---@param whatLinksHere string @A list generated by a dpl of pages in the Template namespace which link to moduleName.
---@return string
local function formatInvokedByList( moduleName, addCategories, whatLinksHere )
local category = addCategories and '[[Category:Template invoked modules]]' or ''
local templateData = enum.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end )
templateData = enum.filter( templateData, function(x) return enum.any( x.invokeList, function(y) return y.moduleName==moduleName end ) end )
if #templateData > 0 then
moduleIsUsed = true
local res = {}
for _, template in ipairs( templateData ) do
for _, invoke in ipairs( template.invokeList ) do
table.insert( res, string.format(
"<div role='note' class='hatnote'><div class=hatnote-container navigation-not-searchable'><span class='hatnote-icon metadata'>[[File:WikimediaUI-Code.svg|14px|link=]]</span>'''%s's''' function '''%s''' is invoked by [[%s]].</div></div>",
moduleName,
invoke.funcName,
template.templateName
) )
end
end
table.sort( res )
table.insert( res, category )
return table.concat( res )
end
return ''
end
---@param moduleName string
---@param addCategories boolean
---@param whatLinksHere string @A list generated by a dpl of pages in the Module namespace which link to moduleName.
---@return string
local function formatRequiredByList( moduleName, addCategories, whatLinksHere )
local childModuleData = enum.map( whatLinksHere, function ( title )
local requireList, loadDataList = getRequireList( title )
return {name=title, requireList=requireList, loadDataList=loadDataList}
end )
local requiredByList = enum.map( childModuleData, function ( item )
if enum.any( item.requireList, function(x) return x==moduleName end ) then
if item.name:find( '%%' ) then
return formatDynamicQueryLink( item.name )
else
return '[[' .. item.name .. ']]'
end
end
end )
local loadedByList = enum.map( childModuleData, function ( item )
if enum.any( item.loadDataList, function(x) return x==moduleName end ) then
if item.name:find( '%%' ) then
return formatDynamicQueryLink( item.name )
else
return '[[' .. item.name .. ']]'
end
end
end )
if #requiredByList > 0 or #loadedByList > 0 then
moduleIsUsed = true
end
local res = {}
for _, requiredByModuleName in ipairs( requiredByList ) do
table.insert( res, string.format(
"<div role='note' class='hatnote'><div class=hatnote-container navigation-not-searchable'><span class='hatnote-icon metadata'>[[File:WikimediaUI-Code.svg|14px|link=]]</span>'''%s''' is required by %s.</div></div>",
moduleName,
requiredByModuleName
) )
end
if #requiredByList > 0 then
table.insert( res, (addCategories and '[[Category:Modules required by modules]]' or '') )
end
for _, loadedByModuleName in ipairs( loadedByList ) do
table.insert( res, string.format(
"<div role='note' class='hatnote'><div class=hatnote-container navigation-not-searchable'><span class='hatnote-icon metadata'>[[File:WikimediaUI-Code.svg|14px|link=]]</span>'''%s''' is loaded by %s.</div></div>",
moduleName,
loadedByModuleName
) )
end
if #loadedByList > 0 then
table.insert( res, (addCategories and '[[Category:Module data]]' or '') )
end
return table.concat( res )
end
function p.main( frame )
local args = frame:getParent().args
return p._main( args[1], args.category, args.isUsed )
end
---@param currentPageName string|nil
---@param addCategories boolean|string|nil
---@return string
function p._main( currentPageName, addCategories, isUsed )
libraryUtil.checkType( 'Module:RequireList._main', 1, currentPageName, 'string', true )
libraryUtil.checkTypeMulti( 'Module:RequireList._main', 2, addCategories, {'boolean', 'string', 'nil'} )
libraryUtil.checkTypeMulti( 'Module:RequireList._main', 3, isUsed, {'boolean', 'string', 'nil'} )
local title = mw.title.getCurrentTitle()
if param.is_empty( currentPageName ) and (
( title.nsText ~= 'Module' and title.nsText ~= 'Template') or
( title.nsText == 'Module' and title.text:find( '^Sandbox/' ) ) ) then
return ''
end
currentPageName = param.default_to( currentPageName, title.fullText )
currentPageName = string.gsub( currentPageName, '/[Dd]oc$', '' )
currentPageName = string.gsub( currentPageName, '_', ' ' )
currentPageName = ucfirst( currentPageName:gsub( ':(.)', function(c) return ':'..c:upper() end ) )
addCategories = yn( param.default_to( addCategories, title.subpageText~='doc' ) )
moduleIsUsed = yn( param.default_to( isUsed, false ) )
if currentPageName:find( '^Template:' ) then
local invokeList = getInvokeCallList( currentPageName )
return formatInvokeCallList( currentPageName, addCategories, invokeList )
end
local invokedByList, requiredByList = dpl.ask( {
namespace = 'Template',
linksto = currentPageName,
distinct = 'strict',
ignorecase = true,
ordermethod = 'title',
allowcachedresults = true,
cacheperiod = 604800 -- One week
}, {
namespace = 'Module',
linksto = currentPageName,
nottitlematch = '%/doc|%sandbox%|' .. currentPageName:gsub( 'Module:', '' ),
distinct = 'strict',
ignorecase = true,
ordermethod = 'title',
allowcachedresults = true,
cacheperiod = 604800 -- One week
} )
invokedByList = formatInvokedByList( currentPageName, addCategories, invokedByList )
requiredByList = formatRequiredByList( currentPageName, addCategories, requiredByList )
local requireList, loadDataList, usedTemplateList = getRequireList( currentPageName )
requireList = enum.map( requireList, function ( moduleName )
if moduleName:find( '%%' ) then
return formatDynamicQueryLink( moduleName )
else
return '[[' .. moduleName .. ']]'
end
end )
loadDataList = enum.map( loadDataList, function ( moduleName )
if moduleName:find( '%%' ) then
return formatDynamicQueryLink( moduleName )
else
return '[[' .. moduleName .. ']]'
end
end )
local res = {}
if not moduleIsUsed then
table.insert( res, messageBoxUnused( currentPageName:gsub( 'Module:', '' ), addCategories ) )
end
table.insert( res, invokedByList )
for _, requiredModuleName in ipairs( requireList ) do
table.insert( res, string.format(
"<div role='note' class='hatnote'><div class=hatnote-container navigation-not-searchable'><span class='hatnote-icon metadata'>[[File:WikimediaUI-Code.svg|14px|link=]]</span>'''%s''' requires %s.</div></div>",
currentPageName,
requiredModuleName
) )
end
if #requireList > 0 then
table.insert( res, (addCategories and '[[Category:Modules requiring modules]]' or '') )
end
for _, loadedModuleName in ipairs( loadDataList ) do
table.insert( res, string.format(
"<div role='note' class='hatnote'><div class=hatnote-container navigation-not-searchable'><span class='hatnote-icon metadata'>[[File:WikimediaUI-Code.svg|14px|link=]]</span>'''%s''' loads data from %s.</div></div>",
currentPageName,
loadedModuleName
) )
end
if #loadDataList > 0 then
table.insert( res, (addCategories and '[[Category:Modules using data]]' or '') )
end
for _, templateName in ipairs( usedTemplateList ) do
table.insert( res, string.format(
"<div role='note' class='hatnote'><div class=hatnote-container navigation-not-searchable'><span class='hatnote-icon metadata'>[[File:WikimediaUI-Code.svg|14px|link=]]</span>'''%s''' transcludes [[%s]] using <samp>frame:preprocess()</samp>.</div></div>",
currentPageName,
templateName
) )
end
table.insert( res, requiredByList )
return table.concat( res )
end
return p
-- </nowiki>