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")
local p = {}
local p = {}
local libraryUtil = require('libraryUtil')
local libraryUtil = require( 'libraryUtil' )
local arr = require('Module:Array')
local arr = require( 'Module:Array' )
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
Line 12: Line 12:


local builtins = {
local builtins = {
["libraryUtil"] = {
    ["libraryUtil"] = {
link = "mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#libraryUtil",
        link = "mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#libraryUtil",
categories = {},
        categories = {},
},
    },
["strict"] = {
    ["strict"] = {
link = "mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#strict",
        link = "mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#strict",
categories = { "[[Category:Strict mode modules]]" },
        categories = { "Strict mode modules" },
},
    },
}
}


-- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'.
--- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'.
local function substVarValue( moduleContent, varName )
---@param content string    The content of the module to search in
local res = moduleContent:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or moduleContent:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or ''
---@param varName string
if res:find( '^(["\'])[Mm]odule:[%S]+%1' ) and not res:find( '%.%.' ) and not res:find( '%%%a' ) then
---@return string
return mw.text.trim( res )
local function substVarValue( content, varName )
else
    local res = content:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or content:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or ''
return ''
    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
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$', '%2' ) -- 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( '^([^:]-:)(.)', function(a,b) return a..string.upper(b) end )


return name
    return name
end
end


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


if builtins[name] then
        if builtins[name] then
return name
            return name
end
        end
end
    end


local module = formatPageName( str )
    local module = formatPageName( str )


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


return module
    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
    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
    else
return f1
        return f1
end
    end
end
end


local function isDynamicPath( str )
local function isDynamicPath( str )
return string.find( str, '%.%.' ) or string.find( str, '%%%a' )
    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[]
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
        local _, _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 table<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 loadJsonDataList = arr{}
    local usedTemplateList = arr{}
    local dynamicRequirelist = arr{}
    local dynamicLoadDataList = arr{}
    local dynamicLoadJsonDataList = arr{}
    local extraCategories = 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
    local function getList( pat1, pat2, list, dynList )
extraCategories = extraCategories:insert( builtins[name].categories, true )
        for match in dualGmatch( content, pat1, pat2 ) do
end
            match = mw.text.trim( match )
end
            local name = extractModuleName( match, content )
end
end


getList( 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)', requireList, dynamicRequirelist )
            if isDynamicPath( name ) then
getList( 'mw%.loadData%s*(%b())', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)', loadDataList, dynamicLoadDataList )
                dynList:insert( getDynamicRequireList( name ), true )
getList( 'mw%.loadJsonData%s*(%b())', 'mw%.loadJsonData%s*((["\'])%s*[Mm]odule:.-%2)', loadJsonDataList, dynamicLoadJsonDataList )
            elseif name ~= '' then
getList( 'pcall%s*%(%s*require%s*,([^%),]+)', nil, requireList, dynamicRequirelist )
                name = formatModuleName( name, true )
getList( 'pcall%s*%(%s*mw%.loadData%s*,([^%),]+)', nil, loadDataList, dynamicLoadDataList )
                table.insert( list, name )
getList( 'pcall%s*%(%s*mw%.loadJsonData%s*,([^%),]+)', nil, loadJsonDataList, dynamicLoadJsonDataList )


requireList = requireList .. dynamicRequirelist
                if builtins[name] then
requireList = requireList:unique()
                    extraCategories = extraCategories:insert( builtins[name].categories, true )
loadDataList = loadDataList .. dynamicLoadDataList .. loadJsonDataList .. dynamicLoadJsonDataList
                end
loadDataList = loadDataList:unique()
            end
extraCategories = extraCategories:unique()
        end
table.sort( requireList )
    end
table.sort( loadDataList )
table.sort( extraCategories )


return {
    getList( 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)', requireList, dynamicRequirelist )
require = requireList,
    getList( 'mw%.loadData%s*(%b())', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)', loadDataList, dynamicLoadDataList )
loadData = loadDataList,
    getList( 'mw%.loadJsonData%s*(%b())', 'mw%.loadJsonData%s*((["\'])%s*[Mm]odule:.-%2)', loadJsonDataList, dynamicLoadJsonDataList )
extraCategories = extraCategories,
    getList( 'pcall%s*%(%s*require%s*,([^%),]+)', nil, requireList, dynamicRequirelist )
}
    getList( 'pcall%s*%(%s*mw%.loadData%s*,([^%),]+)', nil, loadDataList, dynamicLoadDataList )
end
    getList( 'pcall%s*%(%s*mw%.loadJsonData%s*,([^%),]+)', nil, loadJsonDataList, dynamicLoadJsonDataList )
 
local function getUsedTemplatesList( moduleContent )
local usedTemplateList = arr{}


for preprocess in string.gmatch( moduleContent, ':preprocess%s*(%b())' ) do
    if searchForUsedTemplates then
local function recursiveGMatch( str, pat )
        for preprocess in string.gmatch( content, ':preprocess%s*(%b())' ) do
local list = {}
            local function recursiveGMatch( str, pat )
local i = 0
                local list = {}
repeat
                local i = 0
for match in string.gmatch( list[i] or str, pat ) do
                repeat
table.insert( list, match )
                    for match in string.gmatch( list[i] or str, pat ) do
end
                        table.insert( list, match )
i =  i + 1
                    end
until i > #list or i > 100
                    i =  i + 1
                until i > #list or i > 100


i = 0
                i = 0
return function()
                return function()
i = i + 1
                    i = i + 1
return list[i]
                    return list[i]
end
                end
end
            end


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


usedTemplateList = usedTemplateList:unique()
    requireList = requireList .. dynamicRequirelist
table.sort( usedTemplateList )
    requireList = requireList:unique()
    loadDataList = loadDataList .. dynamicLoadDataList
    loadDataList = loadDataList:unique()
    loadJsonDataList = loadJsonDataList .. dynamicLoadJsonDataList
    usedTemplateList = usedTemplateList:unique()
    extraCategories = extraCategories:unique()
    table.sort( requireList )
    table.sort( loadDataList )
    table.sort( loadJsonDataList )
    table.sort( usedTemplateList )
    table.sort( extraCategories )


return usedTemplateList
    return {
        requireList = requireList,
        loadDataList = loadDataList,
        loadJsonDataList = loadJsonDataList,
        usedTemplateList = usedTemplateList,
        extraCategories = extraCategories
    }
end
end


-- Returns a list with module and function names used in all '{{#Invoke:moduleName|funcName}}' found on page 'templateName'.
--- Returns a list with module and function names used in all '{{#Invoke:moduleName|funcName}}' found on page 'templateName'.
local function getInvokeCallList( pageName )
---@param templateName string
local pageContent = mw.title.new( pageName ):getContent()
---@return table<string, string>[]
local invokeList = {}
local function getInvokeCallList( templateName )
    local content = mw.title.new( templateName ):getContent()
    local invokeList = {}


assert( pageContent, string.format( 'Failed to retrieve text content of page "%s"', pageName ) )
    assert( content ~= nil, string.format( '%s does not exist', templateName ) )


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


-- For form calcs invoking the module directly
    -- 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
    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|]' )
        local moduleName = string.match( config, 'module%s*=%s*(.-)[\n|]' )
if param.has_content( moduleName ) then
        if param.has_content( moduleName ) then
moduleName = formatModuleName( moduleName )
            moduleName = formatModuleName( moduleName )
local funcName = string.match( config, 'modulefunc%s*=%s*(.-)[\n|]' ) or 'main'
            local funcName = string.match( config, 'modulefunc%s*=%s*(.-)[\n|]' ) or 'main'
table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
            table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
end
        end
end
    end


invokeList = arr.unique( invokeList, function(x) return x.moduleName..x.funcName 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 )
    table.sort( invokeList, function(x, y) return x.moduleName..x.funcName < y.moduleName..y.funcName end )


return invokeList
    return invokeList
end
end


local function getInvokedByList( moduleName )
---@param pageName string
local whatTemplatesLinkHere = dpl.ask( {
---@param addCategories boolean
namespace = 'Template|Calculator',
---@return string
linksto = moduleName,
local function messageBoxUnused( pageName, addCategories )
distinct = 'strict',
    local html = mw.html.create( 'table' ):addClass( 'messagebox obsolete plainlinks' )
ordermethod = 'title',
    html:tag( 'td' )
allowcachedresults = true,
        :attr( 'width', '40xp' )
cacheperiod = 604800 -- One week
        :wikitext( '[[File:Willow logs (historical).png|center|30px|link=]]' )
} )
    :done()
 
    :tag( 'td' )
local function lcfirst( str )
        :wikitext( "'''This module is unused.'''" )
return string.gsub( str, '^[Mm]odule:.', string.lower )
        :tag( 'div' )
end
            :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()


local invokedByList = {}
    return tostring( html )
 
for _, templateName in ipairs( whatTemplatesLinkHere ) do
local invokeList = getInvokeCallList( templateName )
 
for _, invokeData in ipairs( invokeList ) do
if lcfirst( invokeData.moduleName ) == lcfirst( moduleName ) then
table.insert( invokedByList, { templateName=templateName, funcName=invokeData.funcName } )
end
end
end
 
return invokedByList
end
 
local function messageBoxUnused()
local html = mw.html.create( 'table' ):addClass( 'messagebox obsolete plainlinks' )
html:tag( 'td' )
:attr( 'width', '40xp' )
:wikitext( '[[File:Willow logs (historical).png|center|30px|link=]]' )
:done()
:tag( 'td' )
:wikitext( "'''This module is unused.'''" )
:tag( 'div' )
:css{ ['font-size']='0.85em', ['line-height']='1.45em' }
: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.' )
:done()
:done()
 
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.
---@return string
local function formatInvokedByList( moduleName, addCategories, whatLinksHere )
    local function lcfirst( str )
        return string.gsub( str, '^[Mm]odule:.', string.lower )
    end


for _, templateName in ipairs(pages) do
    local templateData = arr.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end )
if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
    templateData = arr.filter( templateData, function(x)
links:insert( '[['..templateName..']]' )
        return arr.any( x.invokeList, function(y)
else
            return lcfirst(y.moduleName) == lcfirst(moduleName)
links:insert( "'''&#123;&#123;"..templateName.."&#125;&#125;'''" ) -- Magic words don't have a page so make them bold instead
        end )
end
    end )
end


return links
    local invokedByList = {}
end


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


for _, item in ipairs( invokeList ) do
    table.sort( 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 )
    local res = {}
end


local function formatInvokedByList( moduleName, invokedByList )
    if #invokedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
for i, invoke in ipairs( invokedByList ) do
        table.insert( res, string.format(
invokedByList[i] = string.format( "function '''%s''' is invoked by [[%s]]", invoke.funcName, invoke.templateName )
            "<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


table.sort( invokedByList)
    if #templateData > 0 then
        moduleIsUsed = true
        table.insert( res, (addCategories and '[[Category:Template invoked modules]]' or '') )
    end


local res = {}
    return table.concat( res )
 
end
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


return table.concat( res )
---@param moduleName string
end
---@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 = arr.map( whatLinksHere, function ( title )
        local lists = getRequireList( title )
        return {name=title, requireList=lists.requireList, loadDataList=lists.loadDataList .. lists.loadJsonDataList}
    end )


local function formatRequiredByList( moduleName, requiredByLists )
    local requiredByList = arr.map( childModuleData, function ( item )
local requiredByList = formatModuleLinks( requiredByLists.require )
        if arr.any( item.requireList, function(x) return x:lower()==moduleName:lower() end ) then
local loadedByList = formatModuleLinks( requiredByLists.loadData )
            if item.name:find( '%%' ) then
                return formatDynamicQueryLink( item.name )
            else
                return '[[' .. item.name .. ']]'
            end
        end
    end )


if #requiredByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    local loadedByList = arr.map( childModuleData, function ( item )
requiredByList = collapseList( requiredByList, 'requiredBy', 'modules' )
        if arr.any( item.loadDataList, 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 )


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 formatImportList( currentPageName, moduleList, id, message, category )
usedTemplateList = formatTemplateLinks( usedTemplateList )
    if #moduleList > COLLAPSE_LIST_LENGTH_THRESHOLD then
local res = {}
        moduleList = collapseList( moduleList, id, 'modules' )
    end


if #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then
    local res = arr.map( moduleList, function( moduleName )
usedTemplateList = collapseList( usedTemplateList, 'usedTemplates', 'templates' )
        return '<div class="seealso">' .. string.format( message, currentPageName, moduleName ) .. '</div>'
end
    end )


for _, templateName in ipairs( usedTemplateList ) do
    if #moduleList > 0 and category then
table.insert( res, string.format(
        table.insert( res, string.format( '[[Category:%s]]', category ) )
"<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 formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList )
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 #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then
local requiredByListRaw = bucket( 'dependency_list' ).select( 'page_name' ).where( 'require', currentPageName ).run()
        usedTemplateList = collapseList( usedTemplateList, 'usedTemplates', 'templates' )
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 _, templateName in ipairs( usedTemplateList ) do
table.insert( requiredByList, bucketItem.page_name )
        table.insert( res, string.format(
end
            "<div class='seealso'>'''%s''' transcludes %s using <samp>frame:preprocess()</samp>.</div>",
for _, bucketItem in ipairs( loadedByListRaw ) do
            currentPageName,
table.insert( loadedByList, bucketItem.page_name )
            templateName
end
        ) )
    end


requiredByList = arr.unique( requiredByList )
    return table.concat( res )
loadedByList = arr.unique( loadedByList )
table.sort( requiredByList )
table.sort( loadedByList )
 
return {
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 )
    libraryUtil.checkTypeMulti( 'Module:RequireList._main', 2, addCategories, {'boolean', 'string', 'nil'} )
    libraryUtil.checkTypeMulti( 'Module:RequireList._main', 3, isUsed, {'boolean', 'string', 'nil'} )


local requireLists = getRequireLists( moduleContent )
    local title = mw.title.getCurrentTitle()
local usedTemplateList = getUsedTemplatesList( moduleContent )
local requiredByLists = getRequiredByLists( currentPageName )
local invokedByList = getInvokedByList( currentPageName )


setBucketFields( requireLists )
    -- 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


local res = arr{}
    currentPageName = param.default_to( currentPageName, title.fullText )
    currentPageName = string.gsub( currentPageName, '/[Dd]oc$', '' )
    currentPageName = formatPageName( currentPageName )
    addCategories = yn( param.default_to( addCategories, title.subpageText~='doc' ) )
    moduleIsUsed = yn( param.default_to( isUsed, false ) )


res:insert( formatInvokedByList( currentPageName, invokedByList ) )
    if title.text:lower():find( 'sandbox' ) then
res:insert( formatImportList( currentPageName, requireLists.require, 'require', "'''%s''' requires %s." ) )
        moduleIsUsed = true -- Don't show sandbox modules as unused
res:insert( formatImportList( currentPageName, requireLists.loadData, 'loadData', "'''%s''' loads data from %s." ) )
    end
res:insert( formatUsedTemplatesList( currentPageName, usedTemplateList ) )
res:insert( formatRequiredByList( currentPageName, requiredByLists ) )


if addCategories then
    if currentPageName:find( '^Template:' ) or currentPageName:find( '^Calculator:' ) then
res:insert( requireLists.extraCategories, true )
        local invokeList = getInvokeCallList( currentPageName )
        return formatInvokeCallList(currentPageName, addCategories, invokeList)
    end


if #requireLists.require > 0 then
    local whatTemplatesLinkHere, whatModulesLinkHere = dpl.ask( {
res:insert( '[[Category:Modules requiring modules]]')
        namespace = 'Template|Calculator',
end
        linksto = currentPageName,
if #requireLists.loadData > 0 then
        distinct = 'strict',
res:insert( '[[Category:Modules using data]]')
        ignorecase = true,
end
        ordermethod = 'title',
if #requiredByLists.require > 0 then
        allowcachedresults = true,
res:insert( '[[Category:Modules required by modules]]')
        cacheperiod = 604800 -- One week
end
    }, {
if #requiredByLists.loadData > 0 then
        namespace = 'Module',
res:insert( '[[Category:Module data]]')
        linksto = currentPageName,
end
        nottitlematch = '%/doc|Exchange/%|Exchange historical/%|Data/%|' .. currentPageName:gsub( 'Module:', '' ),
if #invokedByList > 0 then
        distinct = 'strict',
res:insert( '[[Category:Template invoked modules]]')
        ignorecase = true,
end
        ordermethod = 'title',
end
        allowcachedresults = true,
        cacheperiod = 604800 -- One week
    } )


if
    local lists = getRequireList( currentPageName, true )
not (
yn( isUsed )
or currentPageName:lower():find( 'sandbox' )
or #requiredByLists.require > 0
or #requiredByLists.loadData > 0
or #invokedByList > 0
)
then
table.insert( res, 1, messageBoxUnused() )


if addCategories then
    local requireList = arr.map( lists.requireList, function ( moduleName )
res:insert( '[[Category:Unused modules]]')
        if moduleName:find( '%%' ) then
end
            return formatDynamicQueryLink( moduleName )
end
        elseif builtins[moduleName] then
            return '[[' .. builtins[moduleName].link .. '|' .. moduleName .. ']]'
        else
            return '[[' .. moduleName .. ']]'
        end
    end )


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


function p.main( frame )
    local loadJsonDataList = arr.map( lists.loadJsonDataList, 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 .. ']]'
        end
    end )


function p._main( currentPageName, addCategories, isUsed )
    local usedTemplateList = arr.map( lists.usedTemplateList, function( templateName )
libraryUtil.checkType( 'Module:RequireList._main', 1, currentPageName, 'string', true )
        if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
libraryUtil.checkTypeMulti( 'Module:RequireList._main', 2, addCategories, {'boolean', 'string', 'nil'} )
            return '[['..templateName..']]'
libraryUtil.checkTypeMulti( 'Module:RequireList._main', 3, isUsed, {'boolean', 'string', 'nil'} )
        else
            return "'''&#123;&#123;"..templateName.."&#125;&#125;'''" -- Magic words don't have a page so make them bold instead
        end
    end )


local title = mw.title.getCurrentTitle()
    local res = {}


-- Leave early if not in module, template or calculator namespace or if module is part of exchange or data groups
    table.insert( res, formatInvokedByList( currentPageName, addCategories, whatTemplatesLinkHere ) )
if param.is_empty( currentPageName ) and (
    table.insert( res, formatImportList( currentPageName, requireList, 'require', "'''%s''' requires %s.", addCategories and 'Modules requiring modules' ) )
( not arr.contains( {'Module', 'Template', 'Calculator'}, title.nsText ) ) or
    table.insert( res, formatImportList( currentPageName, loadDataList, 'loadData', "'''%s''' loads data from %s.", addCategories and 'Modules using data' ) )
( title.nsText == 'Module' and ( arr.contains( {'Exchange', 'Exchange historical', 'Data'}, title.text:match( '^(.-)/' ) ) ) )
    table.insert( res, formatImportList( currentPageName, loadJsonDataList, 'loadJsonData', "'''%s''' loads data from %s.", addCategories and 'Modules using data' ) )
) then
    table.insert( res, formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList ) )
return ''
    table.insert( res, formatRequiredByList( currentPageName, addCategories, whatModulesLinkHere ) )
end


currentPageName = param.default_to( currentPageName, title.fullText )
    if addCategories then
currentPageName = string.gsub( currentPageName, '/[Dd]oc$', '' )
        local extraCategories = arr.map( lists.extraCategories, function( categoryName )
currentPageName = formatPageName( currentPageName )
            return "[[Category:" .. categoryName .. "]]"
        end )


if (addCategories == nil) then
        table.insert( res, table.concat( extraCategories ) )
addCategories = title.subpageText~='doc'
    end
end
addCategories = yn(addCategories)


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
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: