Editing Module:DependencyList

Jump to navigation Jump to search
Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
require("strict")
-- <nowiki>
local p = {}
local p = {}
local libraryUtil = require('libraryUtil')
local libraryUtil = require( 'libraryUtil' )
local arr = require('Module:Array')
local enum = require( 'Module:Enum' )
local yn = require('Module:Yesno')
local yn = require( 'Module:Yesno' )
local param = require('Module:Paramtest')
local param = require( 'Module:Paramtest' )
local dpl = require('Module:DPLlua')
local dpl = require( 'Module:DPLlua' )
local tooltip = require('Module:Tooltip')
local tooltip = require( 'Module:Tooltip' )
local moduleIsUsed = false
local COLLAPSE_LIST_LENGTH_THRESHOLD = 5
local COLLAPSE_LIST_LENGTH_THRESHOLD = 5
local MAX_DYNAMIC_REQUIRE_LIST_LENGTH = 30
local MAX_DYNAMIC_REQUIRE_LIST_LENGTH = 30
local dynamicRequireListQueryCache = {}
local dynamicRequireListQueryCache = {}


local builtins = {
--- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'.
["libraryUtil"] = {
---@param content string    @The content of the module to look in as a string
link = "mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#libraryUtil",
---@param varName string
categories = {},
---@return string
},
local function substVarValue( content, varName )
["strict"] = {
    if varName:find( "%b''" ) or varName:find( '%b""' ) then -- Look for balanced quotes
link = "mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#strict",
        return varName -- Is already a string
categories = { "[[Category:Strict mode modules]]" },
    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 )
-- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'.
        else
local function substVarValue( moduleContent, varName )
            return ''
local res = moduleContent:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or moduleContent:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or ''
        end
if res:find( '^(["\'])[Mm]odule:[%S]+%1' ) and not res:find( '%.%.' ) and not res:find( '%%%a' ) then
    end
return mw.text.trim( res )
else
return ''
end
end
 
local function extractModuleName( capture, moduleContent )
capture = capture:gsub( '^%(%s*(.-)%s*%)$', '%1' )
 
if capture:find( '^(["\']).-%1$' ) then -- Check if it is already a pure string
return capture
elseif capture:find( '^[%a_][%w_]*$' ) then -- Check if if is a single variable
return substVarValue( moduleContent, capture )
end
 
return capture
end
 
local function formatPageName( str )
local name = mw.text.trim(str)
:gsub( '^([\'\"])(.-)%1$', '%2' ) -- Only remove quotes at start and end of string if both are the same type
:gsub( '_', ' ' )
:gsub( '^.', string.upper )
:gsub( '^([^:]-:)(.)', function(a,b) return a..string.upper(b) end )
 
return name
end
 
local function formatModuleName( str, allowBuiltins )
if allowBuiltins then
local name = mw.text.trim(str)
-- Only remove quotes at start and end of string if both are the same type
:gsub([[^(['"])(.-)%1$]], '%2')
 
if builtins[name] then
return name
end
end
 
local module = formatPageName( str )
 
if not string.find( module, '^[Mm]odule:' ) then
module = 'Module:' .. module
end
 
return module
end
 
local function dualGmatch( str, pat1, pat2 )
local f1 = string.gmatch( str, pat1 )
if pat2 then
local f2 = string.gmatch( str, pat2 )
return function()
return f1() or f2()
end
else
return f1
end
end
end


local function isDynamicPath( str )
---@param str string
return string.find( str, '%.%.' ) or string.find( str, '%%%a' )
---@return string
local function formatModuleName( str )
    return (str:gsub( '^([\'\"])(.-)%1$', function(_, x) return x end ) -- Only remove quotes at start and end of string if both are the same type
    :gsub( '_', ' ' )
    :gsub( '^.', string.upper )
    :gsub( ':(.)', function(x) return ':'..x:upper() end ))
end
end


-- Used in case a construct like 'require( "Module:wowee/" .. isTheBest )' is found.
--- 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.
--- 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 )
local function getDynamicRequireList( query )
if query:find( '%.%.' ) then
    query = mw.text.split( query, '..', true )
query = mw.text.split( query, '..', true )
    query = enum.map( query, function(x) return mw.text.trim(x) end )
query = arr.map( query, function(x) return (x:match('^%s*[\'\"](.-)[\'\"]%s*$') or '%') end )
    query = enum.map( query, function(x) return (x:match('^[\'\"]([^\'\"]+)[\'\"]$') or '%') end )
query = table.concat( query )
    query = table.concat( query )
else
    query = query:gsub( '^[Mm]odule:', '' )
local _, _query = query:match( '(["\'])(.-)%1' )
query = _query:gsub( '%%%a', '%%' ) -- Replace lua string.format specifiers with a dpl wildcard
end
query = query:gsub( '^[Mm]odule:', '' )


query = mw.language.getContentLanguage():ucfirst(query)
    if query:find( '^[Ee]xchange/' ) or query:find( '^[Dd]ata/' ) then
if query:find( '^Exchange/' ) or query:find( '^Data/' ) then
        return { 'Module:' .. query }  -- This format will later be used by formatDynamicQueryLink()
return { 'Module:' .. query }  -- This format will later be used by formatDynamicQueryLink()
    end
end


if dynamicRequireListQueryCache[ query ] then
    if dynamicRequireListQueryCache[ query ] then
return dynamicRequireListQueryCache[ query ]
        return dynamicRequireListQueryCache[ query ]
end
    end


local list = dpl.ask{
    local list = dpl.ask{
namespace = 'Module',
        namespace = 'Module',
titlematch = query,
        titlematch = query,
nottitlematch = '%/doc|'..query..'/%',
        nottitlematch = '%/doc|%sandbox%%|'..query..'/%',
distinct = 'strict',
        distinct = 'strict',
ordermethod = 'title',
        ignorecase = true,
count = MAX_DYNAMIC_REQUIRE_LIST_LENGTH + 1,
        ordermethod = 'title',
skipthispage = 'no',
        count = MAX_DYNAMIC_REQUIRE_LIST_LENGTH + 1,
allowcachedresults = true,
        skipthispage = 'no',
cacheperiod = 604800 -- One week
        allowcachedresults = true,
}
        cacheperiod = 604800 -- One week
    }


if #list > MAX_DYNAMIC_REQUIRE_LIST_LENGTH then
    if #list > MAX_DYNAMIC_REQUIRE_LIST_LENGTH then
list = { 'Module:' .. query }
        list = { 'Module:' .. query }
end
    end


dynamicRequireListQueryCache[ query ] = list
    dynamicRequireListQueryCache[ query ] = list


return list
    return list
end
end


--- Returns a list of modules loaded and required by module 'moduleName'.
--- Returns a list of modules loaded and required by module 'moduleName'.
local function getRequireLists( moduleContent )
---@param moduleName string
local requireList = arr{}
---@param searchForUsedTemplates boolean
local loadDataList = arr{}
---@return string[], string[], string[]
local loadJsonDataList = arr{}
local function getRequireList( moduleName, searchForUsedTemplates )
local dynamicRequirelist = arr{}
    local content = mw.title.new( moduleName ):getContent()
local dynamicLoadDataList = arr{}
    local requireList = {}
local dynamicLoadJsonDataList = arr{}
    local loadDataList = {}
local extraCategories = arr{}
    local usedTemplateList = {}
    local dynamicRequirelist = {}
    local dynamicLoadDataList = {}


local function getList( pat1, pat2, list, dynList )
    assert( param.has_content( content ), string.format( '%s does not exist', moduleName ) )
for match in dualGmatch( moduleContent, pat1, pat2 ) do
match = mw.text.trim( match )
local name = extractModuleName( match, moduleContent )


if isDynamicPath( name ) then
    content = content:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments
dynList:insert( getDynamicRequireList( name ), true )
elseif name ~= '' then
name = formatModuleName( name, true )
table.insert( list, name )


if builtins[name] then
    local function dualGmatch( str, pat1, pat2 )
extraCategories = extraCategories:insert( builtins[name].categories, true )
        local f1 = string.gmatch( str, pat1 )
end
        local f2 = string.gmatch( str, pat2 )
end
        return function()
end
            return f1() or f2()
end
        end
    end


getList( 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)', requireList, dynamicRequirelist )
    for match in dualGmatch( content, 'require%s*%(([^%)]+)', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do
getList( 'mw%.loadData%s*(%b())', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)', loadDataList, dynamicLoadDataList )
        match = mw.text.trim( match )
getList( 'mw%.loadJsonData%s*(%b())', 'mw%.loadJsonData%s*((["\'])%s*[Mm]odule:.-%2)', loadJsonDataList, dynamicLoadJsonDataList )
        match = substVarValue( content, match )
getList( 'pcall%s*%(%s*require%s*,([^%),]+)', nil, requireList, dynamicRequirelist )
getList( 'pcall%s*%(%s*mw%.loadData%s*,([^%),]+)', nil, loadDataList, dynamicLoadDataList )
getList( 'pcall%s*%(%s*mw%.loadJsonData%s*,([^%),]+)', nil, loadJsonDataList, dynamicLoadJsonDataList )


requireList = requireList .. dynamicRequirelist
        if match:find( '%.%.' ) then
requireList = requireList:unique()
            for _, x in ipairs( getDynamicRequireList( match ) ) do
loadDataList = loadDataList .. dynamicLoadDataList .. loadJsonDataList .. dynamicLoadJsonDataList
                table.insert( dynamicRequirelist, x )
loadDataList = loadDataList:unique()
            end
extraCategories = extraCategories:unique()
        elseif match ~= '' then
table.sort( requireList )
            match = formatModuleName( match )
table.sort( loadDataList )
table.sort( extraCategories )


return {
            if match == 'LibraryUtil' then
require = requireList,
                match = 'Module:LibraryUtil'
loadData = loadDataList,
            end
extraCategories = extraCategories,
}
end


local function getUsedTemplatesList( moduleContent )
            table.insert( requireList, match )
local usedTemplateList = arr{}
        end
    end


for preprocess in string.gmatch( moduleContent, ':preprocess%s*(%b())' ) do
    for match in dualGmatch( content, 'mw%.loadData%s*%(([^%)]+)', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)' ) do
local function recursiveGMatch( str, pat )
        match = mw.text.trim( match )
local list = {}
        match = substVarValue( content, match )
local i = 0
repeat
for match in string.gmatch( list[i] or str, pat ) do
table.insert( list, match )
end
i = i + 1
until i > #list or i > 100


i = 0
        if match:find( '%.%.' ) then
return function()
            for _, x in ipairs( getDynamicRequireList( match ) ) do
i = i + 1
                table.insert( dynamicLoadDataList, x )
return list[i]
            end
end
        elseif match ~= '' then
end
            match = formatModuleName( match )
            table.insert( loadDataList, match )
        end
    end


for template in recursiveGMatch( preprocess, '{(%b{})}' ) do
    for func, match in string.gmatch( content, 'pcall%s*%(([^,]+),([^%),]+)' ) do
local name = string.match( template, '{(.-)[|{}]' )
        func = mw.text.trim( func )
if name ~= '' then
        match = mw.text.trim( match )
if name:find( ':' ) then
local ns = name:match( '^(.-):' )
if arr.contains( {'', 'template', 'calculator', 'user'}, ns:lower() ) then
table.insert( usedTemplateList, name )
elseif ns == ns:upper() then
table.insert( usedTemplateList, ns ) -- Probably a magic word
end
else
if name:match( '^%u+$' ) or name == '!' then
table.insert( usedTemplateList, name ) -- Probably a magic word
else
table.insert( usedTemplateList, 'Template:'..name )
end
end
end
end
end
 
usedTemplateList = usedTemplateList:unique()
table.sort( usedTemplateList )
 
return usedTemplateList
end


-- Returns a list with module and function names used in all '{{#Invoke:moduleName|funcName}}' found on page 'templateName'.
        if func == 'require' then
local function getInvokeCallList( pageName )
            for _, x in ipairs( getDynamicRequireList( match ) ) do
local pageContent = mw.title.new( pageName ):getContent()
                table.insert( dynamicRequirelist, x )
local invokeList = {}
            end
        elseif func == 'mw.loadData' then
            for _, x in ipairs( getDynamicRequireList( match ) ) do
                table.insert( dynamicLoadDataList, x )
            end
        end
    end


assert( pageContent, string.format( 'Failed to retrieve text content of page "%s"', pageName ) )
    if searchForUsedTemplates then
        for preprocess in string.gmatch( content, ':preprocess%s*(%b())' ) do
            local function recursiveGMatch( str, pat )
                local list = {}
                local i = 0
                repeat
                    for match in string.gmatch( list[i] or str, pat ) do
                        table.insert( list, match )
                    end
                    i =  i + 1
                until i > #list or i > 100


for moduleName, funcName in string.gmatch( pageContent, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
                i = 0
moduleName = formatModuleName( moduleName )
                return function()
funcName = mw.text.trim( funcName )
                    i = i + 1
if string.find( funcName, '^{{{' ) then
                    return list[i]
funcName = funcName ..  '}}}'
                end
end
            end
table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
end


-- For form calcs invoking the module directly
            for template in recursiveGMatch( preprocess, '{(%b{})}' ) do
for config in dualGmatch( pageContent, '<[pd][ri][ev]%s+class%s*=%s*["\']jcConfig["\'](.-)</[pd][ri][ev]>', '{{[Ff]orm calculator%s*|(.+)}}' ) do
                local name = string.match( template, '{(.-)[|{}]' )
local moduleName = string.match( config, 'module%s*=%s*(.-)[\n|]' )
                if name ~= '' then
if param.has_content( moduleName ) then
                    if name:find( ':' ) then
moduleName = formatModuleName( moduleName )
                        local ns = name:match( '^(.-):' )
local funcName = string.match( config, 'modulefunc%s*=%s*(.-)[\n|]' ) or 'main'
                        if enum.contains( {'', 'template', 'calculator', 'user'}, ns:lower() ) then
table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
                            table.insert( usedTemplateList, name )
end
                        elseif ns == ns:upper() then
end
                            table.insert( usedTemplateList, ns ) -- Probably a magic word
                        end
                    else
                        if name:match( '^%u+$' ) or name == '!' then
                            table.insert( usedTemplateList, name ) -- Probably a magic word
                        else
                            table.insert( usedTemplateList, 'Template:'..name )
                        end
                    end
                end
            end
        end
    end


invokeList = arr.unique( invokeList, function(x) return x.moduleName..x.funcName end )
    dynamicRequirelist = enum.reject( dynamicRequirelist, function(x) return (enum.contains(requireList, x) or enum.contains(loadDataList, x)) end )
table.sort( invokeList, function(x, y) return x.moduleName..x.funcName < y.moduleName..y.funcName 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 invokeList
    return requireList, loadDataList, usedTemplateList
end
end


local function getInvokedByList( moduleName )
--- Returns a list with module and function names used in all '{{#Invoke:moduleName|funcName}}' found on page 'templateName'.
local whatTemplatesLinkHere = dpl.ask( {
---@param templateName string
namespace = 'Template|Calculator',
---@return table<string, string>[]
linksto = moduleName,
local function getInvokeCallList( templateName )
distinct = 'strict',
    local content = mw.title.new( templateName ):getContent()
ordermethod = 'title',
    local invokeList = {}
allowcachedresults = true,
cacheperiod = 604800 -- One week
} )
 
local function lcfirst( str )
return string.gsub( str, '^[Mm]odule:.', string.lower )
end


local invokedByList = {}
    assert( param.has_content( content ), string.format( '%s does not exist', templateName ) )


for _, templateName in ipairs( whatTemplatesLinkHere ) do
    for moduleName, funcName in string.gmatch( content, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
local invokeList = getInvokeCallList( templateName )
        moduleName = string.format( 'Module:%s', moduleName )
        moduleName = formatModuleName( moduleName )
        table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
    end


for _, invokeData in ipairs( invokeList ) do
    invokeList = enum.unique( invokeList, function(x) return x.moduleName..x.funcName end )
if lcfirst( invokeData.moduleName ) == lcfirst( moduleName ) then
    table.sort( invokeList, function(x, y) return x.moduleName..x.funcName < y.moduleName..y.funcName end )
table.insert( invokedByList, { templateName=templateName, funcName=invokeData.funcName } )
end
end
end


return invokedByList
    return invokeList
end
end


local function messageBoxUnused()
---@param pageName string
local html = mw.html.create( 'table' ):addClass( 'messagebox obsolete plainlinks' )
---@param addCategories boolean
html:tag( 'td' )
---@return string
:attr( 'width', '40xp' )
local function messageBoxUnused( pageName, addCategories )
:wikitext( '[[File:Willow logs (historical).png|center|30px|link=]]' )
    local html = mw.html.create( 'table' ):addClass( 'messagebox obsolete plainlinks' )
:done()
    html:tag( 'td' )
:tag( 'td' )
        :attr( 'width', '40xp' )
:wikitext( "'''This module is unused.'''" )
        :wikitext( '[[File:Old boot detail.png|center|30px|link=]]' )
:tag( 'div' )
    :done()
:css{ ['font-size']='0.85em', ['line-height']='1.45em' }
    :tag( 'td' )
:wikitext( '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&nbsp;documentation]]}}</code> to the calling template\'s or parent\'s module documentation.' )
        :wikitext( "'''This module is unused.'''" )
:done()
        :tag( 'div' )
:done()
            :css{ ['font-size']='0.85em', ['line-height']='1.45em' }
            :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&nbsp;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 )
    return tostring( html )
end
end


local function collapseList( list, id, listType )
local function collapseList( list, id, listType )
local text = string.format( '%d %s', #list, listType )
    local text = string.format( '%d %s', #list, listType )
local button = tooltip._span{ name=id, alt=text }
    local button = tooltip._span{ name=id, alt=text }
list = arr.map( list, function(x) return '\n# '..x end )
    list = enum.map( list, function(x) return '\n# '..x end )
local content = tooltip._div{ name=id, content='\n'..table.concat( list )..'\n\n' }
    local content = tooltip._div{ name=id, content='\n'..table.concat( list )..'\n\n' }


return { tostring( button ) .. tostring( content ) }
    return { tostring( button ) .. tostring( content ) }
end
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.
--- Creates a link to [[Special:Search]] showing all pages found by getDynamicRequireList() in case it found more than MAX_DYNAMIC_REQUIRE_LIST_LENGTH pages.
-- Input query uses DPL % wildcards like 'Module:Wowee/%' or 'Module:Wowee/%/data'
---@param query string      @This will be in a format like 'Module:Wowee/%' or 'Module:Wowee/%/data'
---@return string
local function formatDynamicQueryLink( query )
local function formatDynamicQueryLink( query )
local prefix = query:match( '^([^/]+)' )
    local prefix = query:match( '^([^/]+)' )
local linkText = query:gsub( '%%', '&lt; ... &gt;' )
    local linkText = query:gsub( '%%', '&lt; ... &gt;' )


query = query:gsub( '^Module:',  '' )
    query = query:gsub( '^Module:',  '' )


query = query:gsub( '([^/]+)/?', function ( match )
    query = query:gsub( '([^/]+)/?', function ( match )
if match == '%' then
        if match == '%' then
return '\\/[^\\/]+'
            return '\\/[^\\/]+'
else
        else
return '\\/"' .. match .. '"'
            return '\\/"' .. match .. '"'
end
        end
end )
    end )


query = query:gsub( '^\\/', '' )
    query = query:gsub( '^\\/', '' )


query = string.format(
    query = string.format(
'intitle:/%s%s/i -intitle:/%s\\/""/i -intitle:doc prefix:"%s"',
        'intitle:/%s%s/i -intitle:/%s\\/""/i -intitle:doc prefix:"%s"',
query,
        query,
query:find( '"$' ) and '' or '""',
        query:find( '"$' ) and '' or '""',
query,
        query,
prefix
        prefix
)
    )


return string.format( '<span class="plainlinks">[%s %s]</span>', tostring( mw.uri.fullUrl( 'Special:Search', { search = query } ) ), linkText )
    return string.format( '<span class="plainlinks">[%s %s]</span>', tostring( mw.uri.fullUrl( 'Special:Search', { search = query } ) ), linkText )
end
end


local function formatModuleLinks( pages )
---@param templateName string
local links = arr{}
---@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 _, moduleName in ipairs(pages) do
    for _, item in ipairs( invokeList ) do
if moduleName:find( '%%' ) then
        table.insert( res, string.format(
links:insert( formatDynamicQueryLink( moduleName ) )
            "<div class='seealso'>'''''%s''' invokes function '''%s''' in [[%s]] using [[RuneScape:Lua|Lua]]''.</div>",
elseif builtins[moduleName] then
            templateName,
links:insert( '[[' .. builtins[moduleName].link .. '|' .. moduleName .. ']]' )
            item.funcName,
else
            item.moduleName
links:insert( '[[' .. moduleName .. ']]' )
        ) )
end
    end
end


return links
    if #invokeList > 0 then
        table.insert( res, category )
    end
 
    return table.concat( res )
end
end


local function formatTemplateLinks( pages )
---@param moduleName string
local links = arr{}
---@param addCategories boolean
---@param whatLinksHere string    @A list generated by a dpl of pages in the Template or Calculator namespace which link to moduleName.
---@return string
local function formatInvokedByList( moduleName, addCategories, whatLinksHere )
    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:lower() == moduleName:lower()
        end )
    end )


for _, templateName in ipairs(pages) do
    local invokedByList = {}
if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
links:insert( '[['..templateName..']]' )
else
links:insert( "'''&#123;&#123;"..templateName.."&#125;&#125;'''" ) -- Magic words don't have a page so make them bold instead
end
end


return links
    for _, template in ipairs( templateData ) do
end
        for _, invoke in ipairs( template.invokeList ) do
            table.insert( invokedByList, string.format( "function '''%s''' is invoked by [[%s]]", invoke.funcName, template.templateName ) )
        end
    end
 
    table.sort( invokedByList)
 
    local res = {}


local function formatInvokeCallList( templateName, invokeList )
    if #invokedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
local res = {}
        table.insert( res, string.format(
            "<div class='seealso'>'''''%s''' is invoked by %s''.</div>",
            moduleName,
            collapseList( invokedByList, 'invokedBy', 'templates' )[1]
        ) )
    else
        for _, item in ipairs( invokedByList ) do
            table.insert( res, string.format(
                "<div class='seealso'>'''''%s's''' %s''.</div>",
                moduleName,
                item
            ) )
        end
    end


for _, item in ipairs( invokeList ) do
    if #templateData > 0 then
table.insert( res, string.format(
        moduleIsUsed = true
"<div class='seealso'>'''%s''' invokes function '''%s''' in [[%s]] using [[RuneScape:Lua|Lua]].</div>",
        table.insert( res, (addCategories and '[[Category:Template invoked modules]]' or '') )
templateName,
    end
item.funcName,
item.moduleName
) )
end


return table.concat( res )
    return table.concat( res )
end
end


local function formatInvokedByList( moduleName, invokedByList )
---@param moduleName string
for i, invoke in ipairs( invokedByList ) do
---@param addCategories boolean
invokedByList[i] = string.format( "function '''%s''' is invoked by [[%s]]", invoke.funcName, invoke.templateName )
---@param whatLinksHere string      @A list generated by a dpl of pages in the Module namespace which link to moduleName.
end
---@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 )


table.sort( invokedByList)
    local requiredByList = enum.map( childModuleData, function ( item )
        if enum.any( item.requireList, function(x) return x:lower()==moduleName:lower() end ) then
            if item.name:find( '%%' ) then
                return formatDynamicQueryLink( item.name )
            else
                return '[[' .. item.name .. ']]'
            end
        end
    end )


local res = {}
    local loadedByList = enum.map( childModuleData, function ( item )
        if enum.any( item.loadDataList, function(x) return x:lower()==moduleName:lower() end ) then
            if item.name:find( '%%' ) then
                return formatDynamicQueryLink( item.name )
            else
                return '[[' .. item.name .. ']]'
            end
        end
    end )


if #invokedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    if #requiredByList > 0 or #loadedByList > 0 then
table.insert( res, string.format(
        moduleIsUsed  = true
"<div class='seealso'>'''%s''' is invoked by %s.</div>",
    end
moduleName,
collapseList( invokedByList, 'invokedBy', 'templates' )[1]
) )
else
for _, item in ipairs( invokedByList ) do
table.insert( res, string.format(
"<div class='seealso'>'''%s's''' %s.</div>",
moduleName,
item
) )
end
end


return table.concat( res )
    if #requiredByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
end
        requiredByList = collapseList( requiredByList, 'requiredBy', 'modules' )
    end


local function formatRequiredByList( moduleName, requiredByLists )
    if #loadedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
local requiredByList = formatModuleLinks( requiredByLists.require )
        loadedByList = collapseList( loadedByList, 'loadedBy', 'modules' )
local loadedByList = formatModuleLinks( requiredByLists.loadData )
    end


if #requiredByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    local res = {}
requiredByList = collapseList( requiredByList, 'requiredBy', 'modules' )
end


if #loadedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    for _, requiredByModuleName in ipairs( requiredByList ) do
loadedByList = collapseList( loadedByList, 'loadedBy', 'modules' )
        table.insert( res, string.format(
end
            "<div class='seealso'>'''''%s''' is required by %s''.</div>",
            moduleName,
            requiredByModuleName
        ) )
    end


local res = {}
    if #requiredByList > 0 then
        table.insert( res, (addCategories and '[[Category:Modules required by modules]]' or '') )
    end


for _, requiredByModuleName in ipairs( requiredByList ) do
    for _, loadedByModuleName in ipairs( loadedByList ) do
table.insert( res, string.format(
        table.insert( res, string.format(
"<div class='seealso'>'''%s''' is required by %s.</div>",
            "<div class='seealso'>'''''%s''' is loaded by %s''.</div>",
moduleName,
            moduleName,
requiredByModuleName
            loadedByModuleName
) )
        ) )
end
    end


for _, loadedByModuleName in ipairs( loadedByList ) do
    if #loadedByList > 0 then
table.insert( res, string.format(
        table.insert( res, (addCategories and '[[Category:Module data]]' or '') )
"<div class='seealso'>'''%s''' is loaded by %s.</div>",
    end
moduleName,
loadedByModuleName
) )
end


return table.concat( res )
    return table.concat( res )
end
end


local function formatImportList( currentPageName, moduleList, id, message )
local function formatRequireList( currentPageName, addCategories, requireList )
moduleList = formatModuleLinks( moduleList )
    local res = {}


if #moduleList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    if #requireList > COLLAPSE_LIST_LENGTH_THRESHOLD then
moduleList = collapseList( moduleList, id, 'modules' )
        requireList = collapseList( requireList, 'require', 'modules' )
end
    end


local res = arr.map( moduleList, function( moduleName )
    for _, requiredModuleName in ipairs( requireList ) do
return '<div class="seealso">' .. string.format( message, currentPageName, moduleName ) .. '</div>'
        table.insert( res, string.format(
end )
            "<div class='seealso'>'''''%s''' requires %s.''</div>",
            currentPageName,
            requiredModuleName
        ) )
    end


return table.concat( res )
    if #requireList > 0 then
        table.insert( res, (addCategories and '[[Category:Modules requiring modules]]' or '') )
    end
 
    return table.concat( res )
end
end


local function formatUsedTemplatesList( currentPageName, usedTemplateList )
local function formatLoadDataList( currentPageName, addCategories, loadDataList )
usedTemplateList = formatTemplateLinks( usedTemplateList )
    local res = {}
local res = {}


if #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    if #loadDataList > COLLAPSE_LIST_LENGTH_THRESHOLD then
usedTemplateList = collapseList( usedTemplateList, 'usedTemplates', 'templates' )
        loadDataList = collapseList( loadDataList, 'loadData', 'modules' )
end
    end


for _, templateName in ipairs( usedTemplateList ) do
    for _, loadedModuleName in ipairs( loadDataList ) do
table.insert( res, string.format(
        table.insert( res, string.format(
"<div class='seealso'>'''%s''' transcludes %s using <samp>frame:preprocess()</samp>.</div>",
            "<div class='seealso'>'''''%s''' loads data from %s.''</div>",
currentPageName,
            currentPageName,
templateName
            loadedModuleName
) )
        ) )
end
    end


return table.concat( res )
    if #loadDataList > 0 then
end
        table.insert( res, (addCategories and '[[Category:Modules using data]]' or '') )
    end


local function setBucketFields( requireLists )
    return table.concat( res )
if mw.title.getCurrentTitle().subpageText ~= 'doc' and (#requireLists.require > 0 or #requireLists.loadData > 0) then
bucket( 'dependency_list' ).put{
require = requireLists.require,
load_data = requireLists.loadData
}
end
end
end


local function getRequiredByLists( currentPageName )
local function formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList )
local requiredByListRaw = bucket( 'dependency_list' ).select( 'page_name' ).where( 'require', currentPageName ).run()
    local res = {}
local loadedByListRaw = bucket( 'dependency_list' ).select( 'page_name' ).where( 'load_data', currentPageName ).run()
local requiredByList = {}
local loadedByList = {}


for _, bucketItem in ipairs( requiredByListRaw ) do
    if #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then
table.insert( requiredByList, bucketItem.page_name )
        usedTemplateList = collapseList( usedTemplateList, 'usedTemplates', 'templates' )
end
    end
for _, bucketItem in ipairs( loadedByListRaw ) do
table.insert( loadedByList, bucketItem.page_name )
end


requiredByList = arr.unique( requiredByList )
    for _, templateName in ipairs( usedTemplateList ) do
loadedByList = arr.unique( loadedByList )
        table.insert( res, string.format(
table.sort( requiredByList )
            "<div class='seealso'>'''''%s''' transcludes %s using <samp>frame:preprocess()</samp>.''</div>",
table.sort( loadedByList )
            currentPageName,
            templateName
        ) )
    end


return {
    return table.concat( res )
require = requiredByList,
loadData = loadedByList
}
end
end


local function templateDependencyList( currentPageName, addCategories )
function p.main( frame )
local invokeList = getInvokeCallList( currentPageName )
    local args = frame:getParent().args
local res = formatInvokeCallList( currentPageName, invokeList )
    return p._main( args[1], args.category, args.isUsed )
 
if addCategories and #invokeList > 0 then
res = res .. '[[Category:Lua-based templates]]'
end
 
return res
end
end


local function moduleDependencyList( currentPageName, addCategories, isUsed )
---@param currentPageName string|nil
local moduleContent = mw.title.new( currentPageName ):getContent()
---@param addCategories boolean|string|nil
assert( moduleContent, string.format( 'Failed to retrieve text content of page "%s"', currentPageName ) )
---@return string
moduleContent = moduleContent:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments
function p._main( currentPageName, addCategories, isUsed )
 
    libraryUtil.checkType( 'Module:RequireList._main', 1, currentPageName, 'string', true )
local requireLists = getRequireLists( moduleContent )
    libraryUtil.checkTypeMulti( 'Module:RequireList._main', 2, addCategories, {'boolean', 'string', 'nil'} )
local usedTemplateList = getUsedTemplatesList( moduleContent )
    libraryUtil.checkTypeMulti( 'Module:RequireList._main', 3, isUsed, {'boolean', 'string', 'nil'} )
local requiredByLists = getRequiredByLists( currentPageName )
local invokedByList = getInvokedByList( currentPageName )
 
setBucketFields( requireLists )
 
local res = arr{}


res:insert( formatInvokedByList( currentPageName, invokedByList ) )
    local title = mw.title.getCurrentTitle()
res:insert( formatImportList( currentPageName, requireLists.require, 'require', "'''%s''' requires %s." ) )
res:insert( formatImportList( currentPageName, requireLists.loadData, 'loadData', "'''%s''' loads data from %s." ) )
res:insert( formatUsedTemplatesList( currentPageName, usedTemplateList ) )
res:insert( formatRequiredByList( currentPageName, requiredByLists ) )


if addCategories then
    -- Leave early if not in module, template or calculator namespace or if module is part of exchange or sandbox groups
res:insert( requireLists.extraCategories, true )
    if param.is_empty( currentPageName ) and (
        ( not enum.contains( {'Module', 'Template', 'Calculator'}, title.nsText ) ) or
        ( title.nsText == 'Module' and ( enum.contains( {'Exchange', 'Exchange historical', 'Sandbox'}, title.text:match( '^(.-)/' ) ) ) )
    ) then
        return ''
    end


if #requireLists.require > 0 then
    currentPageName = param.default_to( currentPageName, title.fullText )
res:insert( '[[Category:Modules requiring modules]]')
    currentPageName = string.gsub( currentPageName, '/[Dd]oc$', '' )
end
    currentPageName = formatModuleName( currentPageName )
if #requireLists.loadData > 0 then
    addCategories = yn( param.default_to( addCategories, title.subpageText~='doc' ) )
res:insert( '[[Category:Modules using data]]')
    moduleIsUsed = yn( param.default_to( isUsed, false ) )
end
if #requiredByLists.require > 0 then
res:insert( '[[Category:Modules required by modules]]')
end
if #requiredByLists.loadData > 0 then
res:insert( '[[Category:Module data]]')
end
if #invokedByList > 0 then
res:insert( '[[Category:Template invoked modules]]')
end
end


if
    if currentPageName:find( '^Template:' ) or currentPageName:find( '^Calculator:' ) then
not (
        local invokeList = getInvokeCallList( currentPageName )
yn( isUsed )
        return formatInvokeCallList( currentPageName, addCategories, invokeList )
or currentPageName:lower():find( 'sandbox' )
    end
or #requiredByLists.require > 0
or #requiredByLists.loadData > 0
or #invokedByList > 0
)
then
table.insert( res, 1, messageBoxUnused() )


if addCategories then
    local whatTemplatesLinkHere, whatModulesLinkHere = dpl.ask( {
res:insert( '[[Category:Unused modules]]')
        namespace = 'Template|Calculator',
end
        linksto = currentPageName,
end
        distinct = 'strict',
        ignorecase = true,
        ordermethod = 'title',
        allowcachedresults = true,
        cacheperiod = 604800 -- One week
    }, {
        namespace = 'Module',
        linksto = currentPageName,
        nottitlematch = '%/doc|%sandbox%|Exchange/%|Exchange historical/%|' .. currentPageName:gsub( 'Module:', '' ),
        distinct = 'strict',
        ignorecase = true,
        ordermethod = 'title',
        allowcachedresults = true,
        cacheperiod = 604800 -- One week
    } )


return table.concat( res )
    local requireList, loadDataList, usedTemplateList = getRequireList( currentPageName, true )
end


function p.main( frame )
    requireList = enum.map( requireList, function ( moduleName )
local args = frame:getParent().args
        if moduleName:find( '%%' ) then
return p._main( args[1], args.category, args.isUsed )
            return formatDynamicQueryLink( moduleName )
end
        else
 
            return '[[' .. moduleName .. ']]'
function p._main( currentPageName, addCategories, isUsed )
        end
libraryUtil.checkType( 'Module:RequireList._main', 1, currentPageName, 'string', true )
    end )
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()
    loadDataList = enum.map( loadDataList, function ( moduleName )
        if moduleName:find( '%%' ) then
            return formatDynamicQueryLink( moduleName )
        else
            return '[[' .. moduleName .. ']]'
        end
    end )


-- Leave early if not in module, template or calculator namespace or if module is part of exchange or data groups
    usedTemplateList = enum.map( usedTemplateList, function( templateName )
if param.is_empty( currentPageName ) and (
        if mw.title.new( templateName ).exists then
( not arr.contains( {'Module', 'Template', 'Calculator'}, title.nsText ) ) or
            return '[['..templateName..']]'
( title.nsText == 'Module' and ( arr.contains( {'Exchange', 'Exchange historical', 'Data'}, title.text:match( '^(.-)/' ) ) ) )
        else
) then
            return "'''&#123;&#123;"..templateName.."&#125;&#125;'''" -- Magic words don't have a page so make them bold instead
return ''
        end
end
    end )


currentPageName = param.default_to( currentPageName, title.fullText )
    local res = {}
currentPageName = string.gsub( currentPageName, '/[Dd]oc$', '' )
currentPageName = formatPageName( currentPageName )


if (addCategories == nil) then
    table.insert( res, formatInvokedByList( currentPageName, addCategories, whatTemplatesLinkHere ) )
addCategories = title.subpageText~='doc'
    table.insert( res, formatRequireList( currentPageName, addCategories, requireList ) )
end
    table.insert( res, formatLoadDataList( currentPageName, addCategories, loadDataList ) )
addCategories = yn(addCategories)
    table.insert( res, formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList ) )
    table.insert( res, formatRequiredByList( currentPageName, addCategories, whatModulesLinkHere ) )


if currentPageName:find( '^Template:' ) or currentPageName:find( '^Calculator:' ) then
    if not moduleIsUsed then
return templateDependencyList( currentPageName, addCategories )
        table.insert( res, 1, messageBoxUnused( currentPageName:gsub( 'Module:', '' ), addCategories ) )
end
    end


return moduleDependencyList( currentPageName, addCategories, isUsed )
    return table.concat( res )
end
end


return p
return p
-- </nowiki>
Please note that all contributions to The Deadlock Wiki are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see Deadlock:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template

Page included on this page: