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 arr = 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 search in
link = "mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#libraryUtil",
---@param varName string
categories = {},
---@return string
},
local function substVarValue( content, varName )
["strict"] = {
    local res = content:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or content:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or ''
link = "mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#strict",
    if res:find( '^(["\'])[Mm]odule:[%S]+%1' ) and not res:find( '%.%.' ) and not res:find( '%%%a' ) then
categories = { "[[Category:Strict mode modules]]" },
        return mw.text.trim( res )
},
    else
}
        return ''
 
    end
-- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'.
local function substVarValue( moduleContent, varName )
local res = moduleContent:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or moduleContent:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or ''
if res:find( '^(["\'])[Mm]odule:[%S]+%1' ) and not res:find( '%.%.' ) and not res:find( '%%%a' ) then
return mw.text.trim( res )
else
return ''
end
end
end


local function extractModuleName( capture, moduleContent )
---@param capture string
capture = capture:gsub( '^%(%s*(.-)%s*%)$', '%1' )
---@param content string    The content of the module to search in
---@return string
local function extractModuleName( capture, content )
    capture = capture:gsub( '^%(%s*(.-)%s*%)$', '%1' )


if capture:find( '^(["\']).-%1$' ) then -- Check if it is already a pure string
    if capture:find( '^(["\']).-%1$' ) then -- Check if it is already a pure string
return capture
        return capture
elseif capture:find( '^[%a_][%w_]*$' ) then -- Check if if is a single variable
    elseif capture:find( '^[%a_][%w_]*$' ) then -- Check if if is a single variable
return substVarValue( moduleContent, capture )
        return substVarValue( content, capture )
end
    end


return capture
    return capture
end
end


---@param str string
---@return string
local function formatPageName( str )
local function formatPageName( str )
local name = mw.text.trim(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( '^([\'\"])(.-)%1$', function(_, x) return x end ) -- Only remove quotes at start and end of string if both are the same type
:gsub( '_', ' ' )
        :gsub( '_', ' ' )
:gsub( '^.', string.upper )
        :gsub( '^.', string.upper )
:gsub( '^([^:]-:)(.)', function(a,b) return a..string.upper(b) end )
        :gsub( ':.', string.upper )


return name
    return name
end
end


local function formatModuleName( str, allowBuiltins )
---@param str string
if allowBuiltins then
---@return string
local name = mw.text.trim(str)
local function formatModuleName( str )
-- Only remove quotes at start and end of string if both are the same type
    local module = formatPageName( str )
:gsub([[^(['"])(.-)%1$]], '%2')


if builtins[name] then
    if not string.find( module, '^[Mm]odule:' ) then
return name
        module = 'Module:' .. module
end
    end
end


local module = formatPageName( str )
    return module
 
if not string.find( module, '^[Mm]odule:' ) then
module = 'Module:' .. module
end
 
return module
end
end


local function dualGmatch( str, pat1, pat2 )
local function dualGmatch( str, pat1, pat2 )
local f1 = string.gmatch( str, pat1 )
    local f1 = string.gmatch( str, pat1 )
if pat2 then
    local f2 = string.gmatch( str, pat2 )
local f2 = string.gmatch( str, pat2 )
    return function()
return function()
        return f1() or f2()
return f1() or f2()
    end
end
else
return f1
end
end
 
local function isDynamicPath( str )
return string.find( str, '%.%.' ) or string.find( str, '%%%a' )
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
    if query:find( '%.%.' ) then
query = mw.text.split( query, '..', true )
        query = mw.text.split( query, '..', true )
query = arr.map( query, function(x) return (x:match('^%s*[\'\"](.-)[\'\"]%s*$') or '%') end )
        query = arr.map( query, function(x) return mw.text.trim(x) end )
query = table.concat( query )
        query = arr.map( query, function(x) return (x:match('^[\'\"](.-)[\'\"]$') or '%') end )
else
        query = table.concat( query )
local _, _query = query:match( '(["\'])(.-)%1' )
    else
query = _query:gsub( '%%%a', '%%' ) -- Replace lua string.format specifiers with a dpl wildcard
        _, query = query:match( '(["\'])(.-)%1' )
end
        query = query:gsub( '%%%a', '%%' )
query = query:gsub( '^[Mm]odule:', '' )
    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|'..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 = arr{}
local dynamicLoadJsonDataList = arr{}
    local loadDataList = arr{}
local extraCategories = arr{}
    local usedTemplateList = arr{}
    local dynamicRequirelist = arr{}
    local dynamicLoadDataList = arr{}


local function getList( pat1, pat2, list, dynList )
    assert( content ~= nil, 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
    for match in dualGmatch( content, 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do
extraCategories = extraCategories:insert( builtins[name].categories, true )
        match = mw.text.trim( match )
end
        match = extractModuleName( match, content )
end
end
end


getList( 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)', requireList, dynamicRequirelist )
        if match:find( '%.%.' ) or match:find( '%%%a' ) then
getList( 'mw%.loadData%s*(%b())', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)', loadDataList, dynamicLoadDataList )
            for _, x in ipairs( getDynamicRequireList( match ) ) do
getList( 'mw%.loadJsonData%s*(%b())', 'mw%.loadJsonData%s*((["\'])%s*[Mm]odule:.-%2)', loadJsonDataList, dynamicLoadJsonDataList )
                table.insert( dynamicRequirelist, x )
getList( 'pcall%s*%(%s*require%s*,([^%),]+)', nil, requireList, dynamicRequirelist )
            end
getList( 'pcall%s*%(%s*mw%.loadData%s*,([^%),]+)', nil, loadDataList, dynamicLoadDataList )
        elseif match ~= '' then
getList( 'pcall%s*%(%s*mw%.loadJsonData%s*,([^%),]+)', nil, loadJsonDataList, dynamicLoadJsonDataList )
            match = formatModuleName( match )
            table.insert( requireList, match )
        end
    end


requireList = requireList .. dynamicRequirelist
    for match in dualGmatch( content, 'mw%.loadData%s*(%b())', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)' ) do
requireList = requireList:unique()
        match = mw.text.trim( match )
loadDataList = loadDataList .. dynamicLoadDataList .. loadJsonDataList .. dynamicLoadJsonDataList
        match = extractModuleName( match, content )
loadDataList = loadDataList:unique()
extraCategories = extraCategories:unique()
table.sort( requireList )
table.sort( loadDataList )
table.sort( extraCategories )


return {
        if match:find( '%.%.' ) or match:find( '%%%a' ) then
require = requireList,
            for _, x in ipairs( getDynamicRequireList( match ) ) do
loadData = loadDataList,
                table.insert( dynamicLoadDataList, x )
extraCategories = extraCategories,
            end
}
        elseif match ~= '' then
end
            match = formatModuleName( match )
            table.insert( loadDataList, match )
        end
    end


local function getUsedTemplatesList( moduleContent )
    for func, match in string.gmatch( content, 'pcall%s*%(([^,]+),([^%),]+)' ) do
local usedTemplateList = arr{}
        func = mw.text.trim( func )
        match = mw.text.trim( match )


for preprocess in string.gmatch( moduleContent, ':preprocess%s*(%b())' ) do
        if func == 'require' then
local function recursiveGMatch( str, pat )
            for _, x in ipairs( getDynamicRequireList( match ) ) do
local list = {}
                table.insert( dynamicRequirelist, x )
local i = 0
            end
repeat
        elseif func == 'mw.loadData' then
for match in string.gmatch( list[i] or str, pat ) do
            for _, x in ipairs( getDynamicRequireList( match ) ) do
table.insert( list, match )
                table.insert( dynamicLoadDataList, x )
end
            end
i =  i + 1
        end
until i > #list or i > 100
    end


i = 0
    if searchForUsedTemplates then
return function()
        for preprocess in string.gmatch( content, ':preprocess%s*(%b())' ) do
i = i + 1
            local function recursiveGMatch( str, pat )
return list[i]
                local list = {}
end
                local i = 0
end
                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 template in recursiveGMatch( preprocess, '{(%b{})}' ) do
                i = 0
local name = string.match( template, '{(.-)[|{}]' )
                return function()
if name ~= '' then
                    i = i + 1
if name:find( ':' ) then
                    return list[i]
local ns = name:match( '^(.-):' )
                end
if arr.contains( {'', 'template', 'calculator', 'user'}, ns:lower() ) then
            end
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()
            for template in recursiveGMatch( preprocess, '{(%b{})}' ) do
table.sort( usedTemplateList )
                local name = string.match( template, '{(.-)[|{}]' )
                if name ~= '' then
                    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
    end


return usedTemplateList
    requireList = requireList .. dynamicRequirelist:reject( loadDataList )
end
    requireList = requireList:unique()
    loadDataList = loadDataList .. dynamicLoadDataList:reject( requireList )
    loadDataList = loadDataList:unique()
    usedTemplateList = usedTemplateList:unique()
    table.sort( requireList )
    table.sort( loadDataList )
    table.sort( usedTemplateList )


-- Returns a list with module and function names used in all '{{#Invoke:moduleName|funcName}}' found on page 'templateName'.
    return requireList, loadDataList, usedTemplateList
local function getInvokeCallList( pageName )
local pageContent = mw.title.new( pageName ):getContent()
local invokeList = {}
 
assert( pageContent, string.format( 'Failed to retrieve text content of page "%s"', pageName ) )
 
for moduleName, funcName in string.gmatch( pageContent, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
moduleName = formatModuleName( moduleName )
funcName = mw.text.trim( funcName )
if string.find( funcName, '^{{{' ) then
funcName = funcName ..  '}}}'
end
table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
end
 
-- For form calcs invoking the module directly
for config in dualGmatch( pageContent, '<[pd][ri][ev]%s+class%s*=%s*["\']jcConfig["\'](.-)</[pd][ri][ev]>', '{{[Ff]orm calculator%s*|(.+)}}' ) do
local moduleName = string.match( config, 'module%s*=%s*(.-)[\n|]' )
if param.has_content( moduleName ) then
moduleName = formatModuleName( moduleName )
local funcName = string.match( config, 'modulefunc%s*=%s*(.-)[\n|]' ) or 'main'
table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
end
end
 
invokeList = arr.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
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 )
    assert( content ~= nil, string.format( '%s does not exist', templateName ) )
return string.gsub( str, '^[Mm]odule:.', string.lower )
end


local invokedByList = {}
    for moduleName, funcName in string.gmatch( content, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
        moduleName = formatModuleName( moduleName )
        funcName = mw.text.trim( funcName )
        if string.find( funcName, '^{{{' ) then
        funcName = funcName ..  '}}}'
        end
        table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
    end


for _, templateName in ipairs( whatTemplatesLinkHere ) do
    -- For form calcs invoking the module directly
local invokeList = getInvokeCallList( templateName )
    for config in dualGmatch( content, '<pre%s+class%s*=%s*["\']jcConfig["\'](.-)</pre>', '{{[Ff]orm calculator%s*|(.+)}}' ) do
        local moduleName = string.match( config, 'module%s*=%s*(.-)[\n|]' )
        if param.has_content( moduleName ) then
            moduleName = formatModuleName( moduleName )
            local funcName = string.match( config, 'modulefunc%s*=%s*(.-)[\n|]' ) or 'main'
            table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
        end
    end


for _, invokeData in ipairs( invokeList ) do
    invokeList = arr.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:Willow logs (historical).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 = arr.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 _, item in ipairs( invokeList ) do
        table.insert( res, string.format(
            "<div class='seealso'>'''%s''' invokes function '''%s''' in [[%s]] using [[RuneScape:Lua|Lua]].</div>",
            templateName,
            item.funcName,
            item.moduleName
        ) )
    end


for _, moduleName in ipairs(pages) do
    if #invokeList > 0 then
if moduleName:find( '%%' ) then
        table.insert( res, category )
links:insert( formatDynamicQueryLink( moduleName ) )
    end
elseif builtins[moduleName] then
links:insert( '[[' .. builtins[moduleName].link .. '|' .. moduleName .. ']]' )
else
links:insert( '[[' .. moduleName .. ']]' )
end
end


return links
    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.
for _, templateName in ipairs(pages) do
---@return string
if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
local function formatInvokedByList( moduleName, addCategories, whatLinksHere )
links:insert( '[['..templateName..']]' )
local function lcfirst( str )
else
return string.gsub( str, '^[Mm]odule:.', string.lower )
links:insert( "'''&#123;&#123;"..templateName.."&#125;&#125;'''" ) -- Magic words don't have a page so make them bold instead
end
end
end


return links
    local templateData = arr.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end )
end
    templateData = arr.filter( templateData, function(x)
 
        return arr.any( x.invokeList, function(y)
local function formatInvokeCallList( templateName, invokeList )
            return lcfirst(y.moduleName) == lcfirst(moduleName)
local res = {}
        end )
    end )


for _, item in ipairs( invokeList ) do
    local invokedByList = {}
table.insert( res, string.format(
"<div class='seealso'>'''%s''' invokes function '''%s''' in [[%s]] using [[RuneScape:Lua|Lua]].</div>",
templateName,
item.funcName,
item.moduleName
) )
end


return table.concat( res )
    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


local function formatInvokedByList( moduleName, invokedByList )
    table.sort( invokedByList)
for i, invoke in ipairs( invokedByList ) do
invokedByList[i] = string.format( "function '''%s''' is invoked by [[%s]]", invoke.funcName, invoke.templateName )
end


table.sort( invokedByList)
    local res = {}


local res = {}
    if #invokedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
        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


if #invokedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    if #templateData > 0 then
table.insert( res, string.format(
        moduleIsUsed = true
"<div class='seealso'>'''%s''' is invoked by %s.</div>",
        table.insert( res, (addCategories and '[[Category:Template invoked modules]]' or '') )
moduleName,
    end
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 )
    return table.concat( res )
end
end


local function formatRequiredByList( moduleName, requiredByLists )
---@param moduleName string
local requiredByList = formatModuleLinks( requiredByLists.require )
---@param addCategories boolean
local loadedByList = formatModuleLinks( requiredByLists.loadData )
---@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 = arr.map( whatLinksHere, function ( title )
        local requireList, loadDataList = getRequireList( title )
        return {name=title, requireList=requireList, loadDataList=loadDataList}
    end )


if #requiredByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    local requiredByList = arr.map( childModuleData, function ( item )
requiredByList = collapseList( requiredByList, 'requiredBy', 'modules' )
        if arr.any( item.requireList, function(x) return x:lower()==moduleName:lower() end ) then
end
            if item.name:find( '%%' ) then
                return formatDynamicQueryLink( item.name )
            else
                return '[[' .. item.name .. ']]'
            end
        end
    end )
 
    local loadedByList = arr.map( childModuleData, function ( item )
        if arr.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 #loadedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    if #requiredByList > 0 or #loadedByList > 0 then
loadedByList = collapseList( loadedByList, 'loadedBy', 'modules' )
        moduleIsUsed  = true
end
    end


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


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


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


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


local function formatImportList( currentPageName, moduleList, id, message )
    if #requiredByList > 0 then
moduleList = formatModuleLinks( moduleList )
        table.insert( res, (addCategories and '[[Category:Modules required by modules]]' or '') )
    end


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


local res = arr.map( moduleList, function( moduleName )
    if #loadedByList > 0 then
return '<div class="seealso">' .. string.format( message, currentPageName, moduleName ) .. '</div>'
        table.insert( res, (addCategories and '[[Category:Module data]]' or '') )
end )
    end


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


local function formatUsedTemplatesList( currentPageName, usedTemplateList )
local function formatRequireList( currentPageName, addCategories, requireList )
usedTemplateList = formatTemplateLinks( usedTemplateList )
    local res = {}
local res = {}


if #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    if #requireList > COLLAPSE_LIST_LENGTH_THRESHOLD then
usedTemplateList = collapseList( usedTemplateList, 'usedTemplates', 'templates' )
        requireList = collapseList( requireList, 'require', 'modules' )
end
    end
 
    for _, requiredModuleName in ipairs( requireList ) do
        table.insert( res, string.format(
            "<div class='seealso'>'''%s''' requires %s.</div>",
            currentPageName,
            requiredModuleName
        ) )
    end


for _, templateName in ipairs( usedTemplateList ) do
    if #requireList > 0 then
table.insert( res, string.format(
        table.insert( res, (addCategories and '[[Category:Modules requiring modules]]' or '') )
"<div class='seealso'>'''%s''' transcludes %s using <samp>frame:preprocess()</samp>.</div>",
    end
currentPageName,
templateName
) )
end


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


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


local function getRequiredByLists( currentPageName )
    if #loadDataList > COLLAPSE_LIST_LENGTH_THRESHOLD then
local requiredByListRaw = bucket( 'dependency_list' ).select( 'page_name' ).where( 'require', currentPageName ).run()
        loadDataList = collapseList( loadDataList, 'loadData', 'modules' )
local loadedByListRaw = bucket( 'dependency_list' ).select( 'page_name' ).where( 'load_data', currentPageName ).run()
    end
local requiredByList = {}
local loadedByList = {}


for _, bucketItem in ipairs( requiredByListRaw ) do
    for _, loadedModuleName in ipairs( loadDataList ) do
table.insert( requiredByList, bucketItem.page_name )
        table.insert( res, string.format(
end
            "<div class='seealso'>'''%s''' loads data from %s.</div>",
for _, bucketItem in ipairs( loadedByListRaw ) do
            currentPageName,
table.insert( loadedByList, bucketItem.page_name )
            loadedModuleName
end
        ) )
    end


requiredByList = arr.unique( requiredByList )
    if #loadDataList > 0 then
loadedByList = arr.unique( loadedByList )
        table.insert( res, (addCategories and '[[Category:Modules using data]]' or '') )
table.sort( requiredByList )
    end
table.sort( loadedByList )


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


local function templateDependencyList( currentPageName, addCategories )
local function formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList )
local invokeList = getInvokeCallList( currentPageName )
    local res = {}
local res = formatInvokeCallList( currentPageName, invokeList )


if addCategories and #invokeList > 0 then
    if #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then
res = res .. '[[Category:Lua-based templates]]'
        usedTemplateList = collapseList( usedTemplateList, 'usedTemplates', 'templates' )
end
    end
 
    for _, templateName in ipairs( usedTemplateList ) do
        table.insert( res, string.format(
            "<div class='seealso'>'''%s''' transcludes %s using <samp>frame:preprocess()</samp>.</div>",
            currentPageName,
            templateName
        ) )
    end


return res
    return table.concat( res )
end
end


local function moduleDependencyList( currentPageName, addCategories, isUsed )
function p.main( frame )
local moduleContent = mw.title.new( currentPageName ):getContent()
    local args = frame:getParent().args
assert( moduleContent, string.format( 'Failed to retrieve text content of page "%s"', currentPageName ) )
    return p._main( args[1], args.category, args.isUsed )
moduleContent = moduleContent:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments
end


local requireLists = getRequireLists( moduleContent )
---@param currentPageName string|nil
local usedTemplateList = getUsedTemplatesList( moduleContent )
---@param addCategories boolean|string|nil
local requiredByLists = getRequiredByLists( currentPageName )
---@return string
local invokedByList = getInvokedByList( currentPageName )
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'} )


setBucketFields( requireLists )
    local title = mw.title.getCurrentTitle()


local res = arr{}
    -- Leave early if not in module, template or calculator namespace or if module is part of exchange or data groups
    if param.is_empty( currentPageName ) and (
        ( not arr.contains( {'Module', 'Template', 'Calculator'}, title.nsText ) ) or
        ( title.nsText == 'Module' and ( arr.contains( {'Exchange', 'Exchange historical', 'Data'}, title.text:match( '^(.-)/' ) ) ) )
    ) then
        return ''
    end


res:insert( formatInvokedByList( currentPageName, invokedByList ) )
    currentPageName = param.default_to( currentPageName, title.fullText )
res:insert( formatImportList( currentPageName, requireLists.require, 'require', "'''%s''' requires %s." ) )
    currentPageName = string.gsub( currentPageName, '/[Dd]oc$', '' )
res:insert( formatImportList( currentPageName, requireLists.loadData, 'loadData', "'''%s''' loads data from %s." ) )
    currentPageName = formatPageName( currentPageName )
res:insert( formatUsedTemplatesList( currentPageName, usedTemplateList ) )
    addCategories = yn( param.default_to( addCategories, title.subpageText~='doc' ) )
res:insert( formatRequiredByList( currentPageName, requiredByLists ) )
    moduleIsUsed = yn( param.default_to( isUsed, false ) )


if addCategories then
    if title.text:lower():find( 'sandbox' ) then
res:insert( requireLists.extraCategories, true )
    moduleIsUsed = true -- Don't show sandbox modules as unused
    end


if #requireLists.require > 0 then
    if currentPageName:find( '^Template:' ) or currentPageName:find( '^Calculator:' ) then
res:insert( '[[Category:Modules requiring modules]]')
        local invokeList = getInvokeCallList( currentPageName )
end
        return formatInvokeCallList( currentPageName, addCategories, invokeList )
if #requireLists.loadData > 0 then
    end
res:insert( '[[Category:Modules using data]]')
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
    local whatTemplatesLinkHere, whatModulesLinkHere = dpl.ask( {
not (
        namespace = 'Template|Calculator',
yn( isUsed )
        linksto = currentPageName,
or currentPageName:lower():find( 'sandbox' )
        distinct = 'strict',
or #requiredByLists.require > 0
        ignorecase = true,
or #requiredByLists.loadData > 0
        ordermethod = 'title',
or #invokedByList > 0
        allowcachedresults = true,
)
        cacheperiod = 604800 -- One week
then
    }, {
table.insert( res, 1, messageBoxUnused() )
        namespace = 'Module',
        linksto = currentPageName,
        nottitlematch = '%/doc|Exchange/%|Exchange historical/%|Data/%|' .. currentPageName:gsub( 'Module:', '' ),
        distinct = 'strict',
        ignorecase = true,
        ordermethod = 'title',
        allowcachedresults = true,
        cacheperiod = 604800 -- One week
    } )


if addCategories then
    local requireList, loadDataList, usedTemplateList = getRequireList( currentPageName, true )
res:insert( '[[Category:Unused modules]]')
end
end


return table.concat( res )
    requireList = arr.map( requireList, function ( moduleName )
end
        if moduleName:find( '%%' ) then
            return formatDynamicQueryLink( moduleName )
        else
            return '[[' .. moduleName .. ']]'
        end
    end )


function p.main( frame )
    loadDataList = arr.map( loadDataList, 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()


-- Leave early if not in module, template or calculator namespace or if module is part of exchange or data groups
    usedTemplateList = arr.map( usedTemplateList, function( templateName )
if param.is_empty( currentPageName ) and (
        if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
( 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: