Module:DependencyList: Difference between revisions
From Deepspace Lore
More actions
No edit summary |
No edit summary |
||
| Line 2: | Line 2: | ||
--- @see https://runescape.wiki/w/Module:DependencyList | --- @see https://runescape.wiki/w/Module:DependencyList | ||
require( | -- <nowiki> | ||
require('strict') | |||
local p = {} | local p = {} | ||
local libraryUtil = require('libraryUtil') | local libraryUtil = require('libraryUtil') | ||
| Line 8: | Line 10: | ||
local yn = require('Module:Yesno') | local yn = require('Module:Yesno') | ||
local param = require('Module:Paramtest') | local param = require('Module:Paramtest') | ||
local | local userError = require('Module:User error') | ||
local hatnote = require('Module:Hatnote')._hatnote | |||
local mHatlist = require('Module:Hatnote list') | |||
local mbox = require('Module:Mbox')._mbox | |||
local tooltip = require('Module:Tooltip') | local tooltip = require('Module:Tooltip') | ||
local | local dpl -- lazy loaded | ||
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 NS_MODULE_NAME = mw.site.namespaces[828].name | |||
local NS_TEMPLATE_NAME = mw.site.namespaces[10].name | |||
local builtins = { | local builtins = { | ||
['libraryUtil'] = { | |||
link = 'mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#libraryUtil', | |||
categories = {}, | |||
}, | |||
['strict'] = { | |||
link = 'mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#strict', | |||
categories = { '[[Category:Strict mode modules]]' }, | |||
}, | |||
} | } | ||
-- | -- ============================================================ | ||
local function substVarValue( moduleContent, varName ) | -- String / name utilities | ||
-- ============================================================ | |||
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 ) | local function extractModuleName(capture, moduleContent) | ||
capture = capture:gsub('^%(%s*(.-)%s*%)$', '%1') | |||
if capture:find('^(["\']).-%1$') then | |||
return capture | |||
elseif capture:find('^[%a_][%w_]*$') then | |||
return substVarValue(moduleContent, capture) | |||
end | |||
return capture | |||
end | end | ||
local function formatPageName( str ) | local function formatPageName(str) | ||
return mw.text.trim(str) | |||
:gsub('^(["\'])(.-)%1$', '%2') | |||
:gsub('_', ' ') | |||
:gsub('^.', string.upper) | |||
:gsub('^([^:]-:)(.)', function(a, b) return a .. string.upper(b) end) | |||
end | end | ||
local function formatModuleName( str, allowBuiltins ) | local function formatModuleName(str, allowBuiltins) | ||
if allowBuiltins then | |||
local name = mw.text.trim(str):gsub('^(["\'])(.-)%1$', '%2') | |||
if builtins[name] then return name end | |||
end | |||
local module = formatPageName(str) | |||
if not module:find('^[Mm]odule:') then | |||
module = NS_MODULE_NAME .. ':' .. module | |||
end | |||
return module | |||
end | end | ||
local function | local function isDynamicPath(str) | ||
return str:find('%.%.') or str:find('%%%a') | |||
end | end | ||
local function | local function multiGmatch(str, ...) | ||
local generators = {} | |||
for i, pat in ipairs({...}) do | |||
generators[i] = string.gmatch(str, pat) | |||
end | |||
local function nextCaptures() | |||
local captures = {generators[1]()} | |||
if #captures > 0 then | |||
return unpack(captures) | |||
elseif #generators > 1 then | |||
table.remove(generators, 1) | |||
return nextCaptures() | |||
end | |||
end | |||
return nextCaptures | |||
end | end | ||
-- | -- ============================================================ | ||
-- | -- Dynamic require list (DPL wildcard queries) | ||
-- ============================================================ | |||
local function getDynamicRequireList(query) | |||
if query:find('%.%.') then | |||
query = mw.text.split(query, '..', true) | |||
query = arr.map(query, function(x) return (x:match('^%s*[\'\"](.-)[\'\"]%s*$') or '%') end) | |||
query = table.concat(query) | |||
else | |||
local _, _query = query:match('(["\'])(.-)%1') | |||
query = _query:gsub('%%%a', '%%') | |||
end | |||
query = query:gsub('^[Mm]odule:', '') | |||
query = mw.language.getContentLanguage():ucfirst(query) | |||
if dynamicRequireListQueryCache[query] then | |||
return dynamicRequireListQueryCache[query] | |||
end | |||
local list = dpl.ask{ | |||
namespace = NS_MODULE_NAME, | |||
titlematch = query, | |||
nottitlematch = '%/doc|' .. query .. '/%', | |||
distinct = 'strict', | |||
ordermethod = 'title', | |||
count = MAX_DYNAMIC_REQUIRE_LIST_LENGTH + 1, | |||
skipthispage = 'no', | |||
allowcachedresults = true, | |||
cacheperiod = 604800, | |||
} | |||
if #list > MAX_DYNAMIC_REQUIRE_LIST_LENGTH then | |||
list = {'Module:' .. query} | |||
end | |||
dynamicRequireListQueryCache[query] = list | |||
return list | |||
end | end | ||
--- | -- ============================================================ | ||
-- Require / loadData list parsing | |||
-- ============================================================ | |||
local function getRequireLists(moduleContent) | |||
local requireList = arr{} | |||
local loadDataList = arr{} | |||
local extraCategories = arr{} | |||
local function getList(list, patterns) | |||
for match in multiGmatch(moduleContent, unpack(patterns)) do | |||
match = mw.text.trim(match) | |||
local name = extractModuleName(match, moduleContent) | |||
if isDynamicPath(name) then | |||
list:insert(getDynamicRequireList(name), true) | |||
elseif name ~= '' then | |||
name = formatModuleName(name, true) | |||
table.insert(list, name) | |||
if builtins[name] then | |||
extraCategories = extraCategories:insert(builtins[name].categories, true) | |||
end | |||
end | |||
end | |||
end | |||
getList(requireList, { | |||
'require%s*(%b())', | |||
'require%s*((["\'])%s*[Mm]odule:.-%2)', | |||
'pcall%s*%(%s*require%s*,([^%),]+)', | |||
}) | |||
getList(loadDataList, { | |||
'mw%.loadData%s*(%b())', | |||
'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)', | |||
'pcall%s*%(%s*mw%.loadData%s*,([^%),]+)', | |||
'mw%.loadJsonData%s*(%b())', | |||
'mw%.loadJsonData%s*((["\'])%s*[Mm]odule:.-%2)', | |||
'pcall%s*%(%s*mw%.loadJsonData%s*,([^%),]+)', | |||
}) | |||
requireList = requireList:unique() | |||
loadDataList = loadDataList:unique() | |||
extraCategories = extraCategories:unique() | |||
table.sort(requireList) | |||
table.sort(loadDataList) | |||
table.sort(extraCategories) | |||
return {require = requireList, loadData = loadDataList, extraCategories = extraCategories} | |||
end | |||
-- ============================================================ | |||
-- TemplateStyles / used-template detection | |||
-- ============================================================ | |||
local function insertTemplateStyle( styleName, | local function insertTemplateStyle(styleName, list) | ||
styleName = formatPageName(styleName) | |||
if not styleName:find(':') then styleName = 'Template:' .. styleName end | |||
if isDynamicPath(styleName) then | |||
list:insert(getDynamicRequireList(styleName), true) | |||
else | |||
list:insert(styleName) | |||
end | |||
end | end | ||
local function extractTemplateStyles( pageContent, | local function extractTemplateStyles(pageContent, list) | ||
for _, styleName in string.gmatch( | |||
pageContent, | |||
'<[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee][Ss][Tt][Yy][Ll][Ee][Ss]%s+[Ss][Rr][Cc]=(["\'])(.-)%1' | |||
) do | |||
styleName = formatPageName(styleName) | |||
if styleName ~= '' then insertTemplateStyle(styleName, list) end | |||
end | |||
end | end | ||
local function recursiveGMatch( str, pat ) | 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 | |||
i = 0 | |||
return function() | |||
i = i + 1 | |||
return list[i] | |||
end | |||
end | end | ||
local function formatTemplate( name ) | local function formatTemplate(name) | ||
if name:find(':') then | |||
local ns = name:match('^(.-):') | |||
if arr.contains({'', 'template', 'user'}, ns:lower()) then | |||
return name | |||
elseif ns == ns:upper() then | |||
return ns | |||
end | |||
else | |||
if name:match('^%u+$') or name == '!' then | |||
return name | |||
else | |||
return 'Template:' .. name | |||
end | |||
end | |||
end | end | ||
local function getUsedTemplatesList( moduleContent ) | local function getUsedTemplatesList(moduleContent) | ||
local usedTemplateList = arr{} | |||
local templateStylesList = arr{} | |||
for preprocess in string.gmatch(moduleContent, ':preprocess%s*(%b())') do | |||
for template in recursiveGMatch(preprocess, '{(%b{})}') do | |||
local name = string.match(template, '{(.-)[|{}]') | |||
if name ~= '' then usedTemplateList:insert(formatTemplate(name)) end | |||
end | |||
extractTemplateStyles(preprocess, templateStylesList) | |||
end | |||
for capture in string.gmatch(moduleContent, 'expandTemplate%s*%(?%s*{%s*title%s*=%s*((["\'])%s*.-%2)') do | |||
local name = formatPageName(capture) | |||
if name ~= '' then usedTemplateList:insert(formatTemplate(name)) end | |||
end | |||
for _, capture in multiGmatch( | |||
moduleContent, | |||
'extensionTag%s*%(%s*' | |||
.. '(["\'])[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee][Ss][Tt][Yy][Ll][Ee][Ss]%1%s*,' | |||
.. '.-,' | |||
.. '%s*{%s*src%s*=%s*((["\'])%s*.-%3)', | |||
'extensionTag%s*%(?%s*{%s*' | |||
.. 'name%s*=%s*(["\'])[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee][Ss][Tt][Yy][Ll][Ee][Ss]%1' | .. 'name%s*=%s*(["\'])[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee][Ss][Tt][Yy][Ll][Ee][Ss]%1' | ||
.. '.-' | .. '.-' | ||
.. 'args%s*=%s*{%s*src%s*=%s*((["\'])%s*.-%3)' | .. 'args%s*=%s*{%s*src%s*=%s*((["\'])%s*.-%3)' | ||
) do | |||
local name = formatPageName(capture) | |||
if name ~= '' then insertTemplateStyle(name, templateStylesList) end | |||
end | |||
usedTemplateList = usedTemplateList:unique() | |||
templateStylesList = templateStylesList:unique() | |||
table.sort(usedTemplateList) | |||
table.sort(templateStylesList) | |||
return {usedTemplateList = usedTemplateList, templateStylesList = templateStylesList} | |||
end | end | ||
-- | -- ============================================================ | ||
-- Template dependency list (invoke + TemplateStyles) | |||
-- ============================================================ | |||
local function getTemplateDependencyList(pageName) | |||
local pageContent = mw.title.new(pageName):getContent() | |||
local invokeList = {} | |||
local templateStylesList = arr{} | |||
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 funcName:find('^{{{') then funcName = funcName .. '}}}' end | |||
table.insert(invokeList, {moduleName = moduleName, funcName = funcName}) | |||
end | |||
extractTemplateStyles(pageContent, templateStylesList) | |||
invokeList = arr.unique(invokeList, function(x) return x.moduleName .. '#' .. x.funcName end) | |||
templateStylesList = templateStylesList:unique() | |||
table.sort(invokeList, function(x, y) | |||
return (x.moduleName .. '#' .. x.funcName) < (y.moduleName .. '#' .. y.funcName) | |||
end) | |||
table.sort(templateStylesList) | |||
return {invokeList = invokeList, templateStylesList = templateStylesList} | |||
end | end | ||
local function getInvokeCallList(pageName) | |||
local function getInvokeCallList( pageName ) | return getTemplateDependencyList(pageName).invokeList | ||
end | end | ||
-- ============================================================ | |||
-- DPL-based "what links here" lookups (from RuneScape module) | |||
-- ============================================================ | |||
local function getWhatLinksHere(pageName, namespace) | |||
return dpl.ask{ | |||
namespace = namespace, | |||
linksto = pageName, | |||
distinct = 'strict', | |||
ignorecase = true, | |||
ordermethod = 'title', | |||
allowcachedresults = true, | |||
cacheperiod = 604800, | |||
} | |||
end | end | ||
local function | local function getInvokedByList(moduleName) | ||
local whatTemplatesLinkHere = getWhatLinksHere(moduleName, NS_TEMPLATE_NAME) | |||
local function | local function lcfirst(str) | ||
return str:gsub('^[Mm]odule:.', string.lower) | |||
end | |||
local invokedByList = {} | |||
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 | end | ||
local function getRequiredByLists(moduleName) | |||
local whatModulesLinkHere = getWhatLinksHere(moduleName, NS_MODULE_NAME) | |||
local function | |||
local requiredByList = arr{} | |||
local loadedByList = arr{} | |||
for _, callerName in ipairs(whatModulesLinkHere) do | |||
if callerName:lower() ~= moduleName:lower() then | |||
local lists = getRequireLists( | |||
(mw.title.new(callerName):getContent() or '') | |||
:gsub('%-%-%[(=-)%[.-%]%1%]', '') | |||
:gsub('%-%-[^\n]*', '') | |||
) | |||
if arr.any(lists.require, function(x) return x:lower() == moduleName:lower() end) then | |||
requiredByList:insert(callerName) | |||
end | |||
if arr.any(lists.loadData, function(x) return x:lower() == moduleName:lower() end) then | |||
loadedByList:insert(callerName) | |||
end | |||
end | |||
end | |||
requiredByList = requiredByList:unique() | |||
loadedByList = loadedByList:unique() | |||
table.sort(requiredByList) | |||
table.sort(loadedByList) | |||
return {require = requiredByList, loadData = loadedByList} | |||
end | end | ||
-- ============================================================ | |||
-- Formatting helpers | |||
-- ============================================================ | |||
local function collapseList(list, id, listType) | |||
local text = string.format('%d %s', #list, listType) | |||
local button = tooltip._span{name = id, alt = text} | |||
list = arr.map(list, function(x) return '\n# ' .. x end) | |||
local content = tooltip._div{name = id, content = '\n' .. table.concat(list) .. '\n\n'} | |||
return {tostring(button) .. tostring(content)} | |||
end | |||
local function formatDynamicQueryLink(query) | |||
local prefix = query:match('^([^/]+)') | |||
local linkText = query:gsub('%%', '< ... >') | |||
query = query:gsub('^Module?:', '') | |||
query = query:gsub('([^/]+)/?', function(match) | |||
if match == '%' then return '\\/[^\\/]+' else return '\\/"' .. match .. '"' end | |||
end) | |||
query = query:gsub('^\\/', '') | |||
query = string.format( | |||
'intitle:/%s%s/i -intitle:/%s\\/""/i -intitle:doc prefix:"%s"', | |||
query, query:find('"$') and '' or '""', query, prefix | |||
) | |||
return string.format('<span class="plainlinks">[%s %s]</span>', | |||
tostring(mw.uri.fullUrl('Special:Search', {search = query})), linkText) | |||
end | end | ||
local function | local function formatModuleLinks(pages) | ||
local links = arr{} | |||
for _, moduleName in ipairs(pages) do | |||
if moduleName:find('%%') then | |||
links:insert(formatDynamicQueryLink(moduleName)) | |||
elseif builtins[moduleName] then | |||
links:insert('[[' .. builtins[moduleName].link .. '|' .. moduleName .. ']]') | |||
else | |||
links:insert('[[' .. moduleName .. ']]') | |||
end | |||
end | |||
return links | |||
end | |||
local function formatTemplateLinks(pages) | |||
local links = arr{} | |||
for _, templateName in ipairs(pages) do | |||
if templateName:find(':') then | |||
links:insert('[[' .. templateName .. ']]') | |||
else | |||
links:insert("'''{{" .. templateName .. "}}'''") | |||
end | |||
end | |||
return links | |||
end | end | ||
local function formatTemplateStyleLinks( pages, dynamic ) | local function formatTemplateStyleLinks(pages, dynamic) | ||
local links = arr{} | |||
for _, stylesName in ipairs(pages) do | |||
if dynamic and stylesName:find('%%') then | |||
links:insert(formatDynamicQueryLink(stylesName)) | |||
else | |||
links:insert('[[' .. stylesName .. ']]') | |||
end | |||
end | |||
return links | |||
end | end | ||
local function formatTemplateStylesList(callerName, templateStylesList, forModule) | |||
templateStylesList = formatTemplateStyleLinks(templateStylesList, forModule) | |||
local res = {} | |||
if #templateStylesList > COLLAPSE_LIST_LENGTH_THRESHOLD then | |||
local function formatTemplateStylesList( callerName, templateStylesList, forModule ) | templateStylesList = collapseList(templateStylesList, 'templateStyles', 'styles') | ||
end | |||
for _, item in ipairs(templateStylesList) do | |||
table.insert(res, string.format( | |||
"<div class='seealso'>'''%s''' uses styles from %s using [[mw:Special:MyLanguage/Help:TemplateStyles|TemplateStyles]].</div>", | |||
callerName, item | |||
)) | |||
end | |||
return table.concat(res) | |||
end | end | ||
local function formatInvokeCallList( templateName, invokeList ) | local function formatInvokeCallList(templateName, invokeList) | ||
local res = {} | |||
for _, item in ipairs(invokeList) do | |||
table.insert(res, string.format( | |||
"<div class='seealso'>'''%s''' invokes function '''%s''' in [[%s]] using [[mw:Special:MyLanguage/Extension:Scribunto|Lua]].</div>", | |||
templateName, item.funcName, item.moduleName | |||
)) | |||
end | |||
return table.concat(res) | |||
end | end | ||
local function formatInvokedByList( moduleName, invokedByList ) | local function formatInvokedByList(moduleName, invokedByList) | ||
local items = {} | |||
for _, invoke in ipairs(invokedByList) do | |||
table.insert(items, string.format("function '''%s''' is invoked by [[%s]]", invoke.funcName, invoke.templateName)) | |||
end | |||
table.sort(items) | |||
local res = {} | |||
if #items > COLLAPSE_LIST_LENGTH_THRESHOLD then | |||
table.insert(res, string.format( | |||
"<div class='seealso'>'''%s''' is invoked by %s.</div>", | |||
moduleName, collapseList(items, 'invokedBy', 'templates')[1] | |||
)) | |||
else | |||
for _, item in ipairs(items) do | |||
table.insert(res, string.format( | |||
"<div class='seealso'>'''%s's''' %s.</div>", | |||
moduleName, item | |||
)) | |||
end | |||
end | |||
return table.concat(res) | |||
end | end | ||
local function formatRequiredByList( moduleName, requiredByLists ) | local function formatRequiredByList(moduleName, requiredByLists) | ||
local requiredByList = formatModuleLinks(requiredByLists.require) | |||
local loadedByList = formatModuleLinks(requiredByLists.loadData) | |||
if #requiredByList > COLLAPSE_LIST_LENGTH_THRESHOLD then | |||
requiredByList = collapseList(requiredByList, 'requiredBy', 'modules') | |||
end | |||
if #loadedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then | |||
loadedByList = collapseList(loadedByList, 'loadedBy', 'modules') | |||
end | |||
local res = {} | |||
for _, name in ipairs(requiredByList) do | |||
table.insert(res, string.format("<div class='seealso'>'''%s''' is required by %s.</div>", moduleName, name)) | |||
end | |||
for _, name in ipairs(loadedByList) do | |||
table.insert(res, string.format("<div class='seealso'>'''%s''' is loaded by %s.</div>", moduleName, name)) | |||
end | |||
return table.concat(res) | |||
end | end | ||
local function formatImportList( currentPageName, moduleList, id, message ) | local function formatImportList(currentPageName, moduleList, id, message) | ||
moduleList = formatModuleLinks(moduleList) | |||
if #moduleList > COLLAPSE_LIST_LENGTH_THRESHOLD then | |||
moduleList = collapseList(moduleList, id, 'modules') | |||
end | |||
local res = arr.map(moduleList, function(moduleName) | |||
return '<div class="seealso">' .. string.format(message, currentPageName, moduleName) .. '</div>' | |||
end) | |||
return table.concat(res) | |||
end | end | ||
local function formatUsedTemplatesList( currentPageName, usedTemplateList ) | local function formatUsedTemplatesList(currentPageName, usedTemplateList) | ||
usedTemplateList = formatTemplateLinks(usedTemplateList) | |||
local res = {} | |||
if #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then | |||
usedTemplateList = collapseList(usedTemplateList, 'usedTemplates', 'templates') | |||
end | |||
for _, templateName in ipairs(usedTemplateList) do | |||
table.insert(res, string.format( | |||
"<div class='seealso'>'''%s''' transcludes %s using <samp>frame:preprocess()</samp> or <samp>frame:expandTemplate()</samp>.</div>", | |||
currentPageName, templateName | |||
)) | |||
end | |||
return table.concat(res) | |||
end | end | ||
local function | local function messageBoxUnused() | ||
local html = mw.html.create('table'):addClass('messagebox obsolete plainlinks') | |||
html:tag('td') | |||
:attr('width', '40px') | |||
:wikitext('[[File:WikimediaUI-Alert.svg|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.') | |||
:done() | |||
:done() | |||
return tostring(html) | |||
end | end | ||
-- ============================================================ | |||
-- Main entry points | |||
-- ============================================================ | |||
local function templateDependencyList( currentPageName, addCategories ) | local function templateDependencyList(currentPageName, addCategories) | ||
local dependencyList = getTemplateDependencyList(currentPageName) | |||
local res = arr{} | |||
res:insert(formatInvokeCallList(currentPageName, dependencyList.invokeList)) | |||
res:insert(formatTemplateStylesList(currentPageName, dependencyList.templateStylesList)) | |||
if addCategories then | |||
if #dependencyList.templateStylesList > 0 then | |||
res:insert('[[Category:Templates using TemplateStyles]]') | |||
end | |||
if #dependencyList.invokeList > 0 then | |||
res:insert('[[Category:Lua-based templates]]') | |||
end | |||
end | |||
return table.concat(res) | |||
end | end | ||
local function moduleDependencyList( currentPageName, addCategories, isUsed ) | local function moduleDependencyList(currentPageName, addCategories, isUsed) | ||
local moduleContent = mw.title.new(currentPageName):getContent() | |||
assert(moduleContent, string.format('Failed to retrieve text content of page "%s"', currentPageName)) | |||
moduleContent = moduleContent:gsub('%-%-%[(=-)%[.-%]%1%]', ''):gsub('%-%-[^\n]*', '') | |||
local requireLists = getRequireLists(moduleContent) | |||
local usedTemplateList = getUsedTemplatesList(moduleContent) | |||
local requiredByLists = getRequiredByLists(currentPageName) | |||
local invokedByList = getInvokedByList(currentPageName) | |||
local res = arr{} | |||
res:insert(formatInvokedByList(currentPageName, invokedByList)) | |||
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.usedTemplateList)) | |||
res:insert(formatTemplateStylesList(currentPageName, usedTemplateList.templateStylesList, true)) | |||
res:insert(formatRequiredByList(currentPageName, requiredByLists)) | |||
if addCategories then | |||
res:insert(requireLists.extraCategories, true) | |||
if #usedTemplateList.templateStylesList > 0 then res:insert('[[Category:Modules using TemplateStyles]]') end | |||
if #requireLists.require > 0 then res:insert('[[Category:Modules requiring modules]]') end | |||
if #requireLists.loadData > 0 then 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 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 res:insert('[[Category:Unused modules]]') end | |||
end | |||
return table.concat(res) | |||
end | |||
function p.main(frame) | |||
local args = frame:getParent().args | |||
return p._main(args[1], args.category, args.isUsed) | |||
end | end | ||
function p. | function p._main(currentPageName, addCategories, isUsed) | ||
libraryUtil.checkType('Module:DependencyList._main', 1, currentPageName, 'string', true) | |||
libraryUtil.checkTypeMulti('Module:DependencyList._main', 2, addCategories, {'boolean', 'string', 'nil'}) | |||
libraryUtil.checkTypeMulti('Module:DependencyList._main', 3, isUsed, {'boolean', 'string', 'nil'}) | |||
local title = mw.title.getCurrentTitle() | |||
if param.is_empty(currentPageName) and | |||
not arr.contains({NS_MODULE_NAME, NS_TEMPLATE_NAME}, title.nsText) then | |||
return '' | |||
end | |||
currentPageName = param.default_to(currentPageName, title.fullText) | |||
currentPageName = currentPageName:gsub('/[Dd]oc$', '') | |||
currentPageName = formatPageName(currentPageName) | |||
if addCategories == nil then | |||
addCategories = title.subpageText ~= 'doc' | |||
end | |||
addCategories = yn(addCategories) | |||
dpl = require('Module:DPLlua') | |||
if currentPageName:find('^' .. NS_TEMPLATE_NAME .. ':') then | |||
return templateDependencyList(currentPageName, addCategories) | |||
end | |||
return moduleDependencyList(currentPageName, addCategories, isUsed) | |||
end | end | ||
return p | return p | ||
-- </nowiki> | |||
Revision as of 16:31, 24 April 2026
Documentation for this module may be created at Module:DependencyList/doc
--- Based on Module:DependencyList from RuneScape Wiki and Star Citizen Wiki
--- @see https://runescape.wiki/w/Module:DependencyList
-- <nowiki>
require('strict')
local p = {}
local libraryUtil = require('libraryUtil')
local arr = require('Module:Array')
local yn = require('Module:Yesno')
local param = require('Module:Paramtest')
local userError = require('Module:User error')
local hatnote = require('Module:Hatnote')._hatnote
local mHatlist = require('Module:Hatnote list')
local mbox = require('Module:Mbox')._mbox
local tooltip = require('Module:Tooltip')
local dpl -- lazy loaded
local COLLAPSE_LIST_LENGTH_THRESHOLD = 5
local MAX_DYNAMIC_REQUIRE_LIST_LENGTH = 30
local dynamicRequireListQueryCache = {}
local NS_MODULE_NAME = mw.site.namespaces[828].name
local NS_TEMPLATE_NAME = mw.site.namespaces[10].name
local builtins = {
['libraryUtil'] = {
link = 'mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#libraryUtil',
categories = {},
},
['strict'] = {
link = 'mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#strict',
categories = { '[[Category:Strict mode modules]]' },
},
}
-- ============================================================
-- String / name utilities
-- ============================================================
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
local function extractModuleName(capture, moduleContent)
capture = capture:gsub('^%(%s*(.-)%s*%)$', '%1')
if capture:find('^(["\']).-%1$') then
return capture
elseif capture:find('^[%a_][%w_]*$') then
return substVarValue(moduleContent, capture)
end
return capture
end
local function formatPageName(str)
return mw.text.trim(str)
:gsub('^(["\'])(.-)%1$', '%2')
:gsub('_', ' ')
:gsub('^.', string.upper)
:gsub('^([^:]-:)(.)', function(a, b) return a .. string.upper(b) end)
end
local function formatModuleName(str, allowBuiltins)
if allowBuiltins then
local name = mw.text.trim(str):gsub('^(["\'])(.-)%1$', '%2')
if builtins[name] then return name end
end
local module = formatPageName(str)
if not module:find('^[Mm]odule:') then
module = NS_MODULE_NAME .. ':' .. module
end
return module
end
local function isDynamicPath(str)
return str:find('%.%.') or str:find('%%%a')
end
local function multiGmatch(str, ...)
local generators = {}
for i, pat in ipairs({...}) do
generators[i] = string.gmatch(str, pat)
end
local function nextCaptures()
local captures = {generators[1]()}
if #captures > 0 then
return unpack(captures)
elseif #generators > 1 then
table.remove(generators, 1)
return nextCaptures()
end
end
return nextCaptures
end
-- ============================================================
-- Dynamic require list (DPL wildcard queries)
-- ============================================================
local function getDynamicRequireList(query)
if query:find('%.%.') then
query = mw.text.split(query, '..', true)
query = arr.map(query, function(x) return (x:match('^%s*[\'\"](.-)[\'\"]%s*$') or '%') end)
query = table.concat(query)
else
local _, _query = query:match('(["\'])(.-)%1')
query = _query:gsub('%%%a', '%%')
end
query = query:gsub('^[Mm]odule:', '')
query = mw.language.getContentLanguage():ucfirst(query)
if dynamicRequireListQueryCache[query] then
return dynamicRequireListQueryCache[query]
end
local list = dpl.ask{
namespace = NS_MODULE_NAME,
titlematch = query,
nottitlematch = '%/doc|' .. query .. '/%',
distinct = 'strict',
ordermethod = 'title',
count = MAX_DYNAMIC_REQUIRE_LIST_LENGTH + 1,
skipthispage = 'no',
allowcachedresults = true,
cacheperiod = 604800,
}
if #list > MAX_DYNAMIC_REQUIRE_LIST_LENGTH then
list = {'Module:' .. query}
end
dynamicRequireListQueryCache[query] = list
return list
end
-- ============================================================
-- Require / loadData list parsing
-- ============================================================
local function getRequireLists(moduleContent)
local requireList = arr{}
local loadDataList = arr{}
local extraCategories = arr{}
local function getList(list, patterns)
for match in multiGmatch(moduleContent, unpack(patterns)) do
match = mw.text.trim(match)
local name = extractModuleName(match, moduleContent)
if isDynamicPath(name) then
list:insert(getDynamicRequireList(name), true)
elseif name ~= '' then
name = formatModuleName(name, true)
table.insert(list, name)
if builtins[name] then
extraCategories = extraCategories:insert(builtins[name].categories, true)
end
end
end
end
getList(requireList, {
'require%s*(%b())',
'require%s*((["\'])%s*[Mm]odule:.-%2)',
'pcall%s*%(%s*require%s*,([^%),]+)',
})
getList(loadDataList, {
'mw%.loadData%s*(%b())',
'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)',
'pcall%s*%(%s*mw%.loadData%s*,([^%),]+)',
'mw%.loadJsonData%s*(%b())',
'mw%.loadJsonData%s*((["\'])%s*[Mm]odule:.-%2)',
'pcall%s*%(%s*mw%.loadJsonData%s*,([^%),]+)',
})
requireList = requireList:unique()
loadDataList = loadDataList:unique()
extraCategories = extraCategories:unique()
table.sort(requireList)
table.sort(loadDataList)
table.sort(extraCategories)
return {require = requireList, loadData = loadDataList, extraCategories = extraCategories}
end
-- ============================================================
-- TemplateStyles / used-template detection
-- ============================================================
local function insertTemplateStyle(styleName, list)
styleName = formatPageName(styleName)
if not styleName:find(':') then styleName = 'Template:' .. styleName end
if isDynamicPath(styleName) then
list:insert(getDynamicRequireList(styleName), true)
else
list:insert(styleName)
end
end
local function extractTemplateStyles(pageContent, list)
for _, styleName in string.gmatch(
pageContent,
'<[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee][Ss][Tt][Yy][Ll][Ee][Ss]%s+[Ss][Rr][Cc]=(["\'])(.-)%1'
) do
styleName = formatPageName(styleName)
if styleName ~= '' then insertTemplateStyle(styleName, list) end
end
end
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
i = 0
return function()
i = i + 1
return list[i]
end
end
local function formatTemplate(name)
if name:find(':') then
local ns = name:match('^(.-):')
if arr.contains({'', 'template', 'user'}, ns:lower()) then
return name
elseif ns == ns:upper() then
return ns
end
else
if name:match('^%u+$') or name == '!' then
return name
else
return 'Template:' .. name
end
end
end
local function getUsedTemplatesList(moduleContent)
local usedTemplateList = arr{}
local templateStylesList = arr{}
for preprocess in string.gmatch(moduleContent, ':preprocess%s*(%b())') do
for template in recursiveGMatch(preprocess, '{(%b{})}') do
local name = string.match(template, '{(.-)[|{}]')
if name ~= '' then usedTemplateList:insert(formatTemplate(name)) end
end
extractTemplateStyles(preprocess, templateStylesList)
end
for capture in string.gmatch(moduleContent, 'expandTemplate%s*%(?%s*{%s*title%s*=%s*((["\'])%s*.-%2)') do
local name = formatPageName(capture)
if name ~= '' then usedTemplateList:insert(formatTemplate(name)) end
end
for _, capture in multiGmatch(
moduleContent,
'extensionTag%s*%(%s*'
.. '(["\'])[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee][Ss][Tt][Yy][Ll][Ee][Ss]%1%s*,'
.. '.-,'
.. '%s*{%s*src%s*=%s*((["\'])%s*.-%3)',
'extensionTag%s*%(?%s*{%s*'
.. 'name%s*=%s*(["\'])[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee][Ss][Tt][Yy][Ll][Ee][Ss]%1'
.. '.-'
.. 'args%s*=%s*{%s*src%s*=%s*((["\'])%s*.-%3)'
) do
local name = formatPageName(capture)
if name ~= '' then insertTemplateStyle(name, templateStylesList) end
end
usedTemplateList = usedTemplateList:unique()
templateStylesList = templateStylesList:unique()
table.sort(usedTemplateList)
table.sort(templateStylesList)
return {usedTemplateList = usedTemplateList, templateStylesList = templateStylesList}
end
-- ============================================================
-- Template dependency list (invoke + TemplateStyles)
-- ============================================================
local function getTemplateDependencyList(pageName)
local pageContent = mw.title.new(pageName):getContent()
local invokeList = {}
local templateStylesList = arr{}
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 funcName:find('^{{{') then funcName = funcName .. '}}}' end
table.insert(invokeList, {moduleName = moduleName, funcName = funcName})
end
extractTemplateStyles(pageContent, templateStylesList)
invokeList = arr.unique(invokeList, function(x) return x.moduleName .. '#' .. x.funcName end)
templateStylesList = templateStylesList:unique()
table.sort(invokeList, function(x, y)
return (x.moduleName .. '#' .. x.funcName) < (y.moduleName .. '#' .. y.funcName)
end)
table.sort(templateStylesList)
return {invokeList = invokeList, templateStylesList = templateStylesList}
end
local function getInvokeCallList(pageName)
return getTemplateDependencyList(pageName).invokeList
end
-- ============================================================
-- DPL-based "what links here" lookups (from RuneScape module)
-- ============================================================
local function getWhatLinksHere(pageName, namespace)
return dpl.ask{
namespace = namespace,
linksto = pageName,
distinct = 'strict',
ignorecase = true,
ordermethod = 'title',
allowcachedresults = true,
cacheperiod = 604800,
}
end
local function getInvokedByList(moduleName)
local whatTemplatesLinkHere = getWhatLinksHere(moduleName, NS_TEMPLATE_NAME)
local function lcfirst(str)
return str:gsub('^[Mm]odule:.', string.lower)
end
local invokedByList = {}
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 getRequiredByLists(moduleName)
local whatModulesLinkHere = getWhatLinksHere(moduleName, NS_MODULE_NAME)
local requiredByList = arr{}
local loadedByList = arr{}
for _, callerName in ipairs(whatModulesLinkHere) do
if callerName:lower() ~= moduleName:lower() then
local lists = getRequireLists(
(mw.title.new(callerName):getContent() or '')
:gsub('%-%-%[(=-)%[.-%]%1%]', '')
:gsub('%-%-[^\n]*', '')
)
if arr.any(lists.require, function(x) return x:lower() == moduleName:lower() end) then
requiredByList:insert(callerName)
end
if arr.any(lists.loadData, function(x) return x:lower() == moduleName:lower() end) then
loadedByList:insert(callerName)
end
end
end
requiredByList = requiredByList:unique()
loadedByList = loadedByList:unique()
table.sort(requiredByList)
table.sort(loadedByList)
return {require = requiredByList, loadData = loadedByList}
end
-- ============================================================
-- Formatting helpers
-- ============================================================
local function collapseList(list, id, listType)
local text = string.format('%d %s', #list, listType)
local button = tooltip._span{name = id, alt = text}
list = arr.map(list, function(x) return '\n# ' .. x end)
local content = tooltip._div{name = id, content = '\n' .. table.concat(list) .. '\n\n'}
return {tostring(button) .. tostring(content)}
end
local function formatDynamicQueryLink(query)
local prefix = query:match('^([^/]+)')
local linkText = query:gsub('%%', '< ... >')
query = query:gsub('^Module?:', '')
query = query:gsub('([^/]+)/?', function(match)
if match == '%' then return '\\/[^\\/]+' else return '\\/"' .. match .. '"' end
end)
query = query:gsub('^\\/', '')
query = string.format(
'intitle:/%s%s/i -intitle:/%s\\/""/i -intitle:doc prefix:"%s"',
query, query:find('"$') and '' or '""', query, prefix
)
return string.format('<span class="plainlinks">[%s %s]</span>',
tostring(mw.uri.fullUrl('Special:Search', {search = query})), linkText)
end
local function formatModuleLinks(pages)
local links = arr{}
for _, moduleName in ipairs(pages) do
if moduleName:find('%%') then
links:insert(formatDynamicQueryLink(moduleName))
elseif builtins[moduleName] then
links:insert('[[' .. builtins[moduleName].link .. '|' .. moduleName .. ']]')
else
links:insert('[[' .. moduleName .. ']]')
end
end
return links
end
local function formatTemplateLinks(pages)
local links = arr{}
for _, templateName in ipairs(pages) do
if templateName:find(':') then
links:insert('[[' .. templateName .. ']]')
else
links:insert("'''{{" .. templateName .. "}}'''")
end
end
return links
end
local function formatTemplateStyleLinks(pages, dynamic)
local links = arr{}
for _, stylesName in ipairs(pages) do
if dynamic and stylesName:find('%%') then
links:insert(formatDynamicQueryLink(stylesName))
else
links:insert('[[' .. stylesName .. ']]')
end
end
return links
end
local function formatTemplateStylesList(callerName, templateStylesList, forModule)
templateStylesList = formatTemplateStyleLinks(templateStylesList, forModule)
local res = {}
if #templateStylesList > COLLAPSE_LIST_LENGTH_THRESHOLD then
templateStylesList = collapseList(templateStylesList, 'templateStyles', 'styles')
end
for _, item in ipairs(templateStylesList) do
table.insert(res, string.format(
"<div class='seealso'>'''%s''' uses styles from %s using [[mw:Special:MyLanguage/Help:TemplateStyles|TemplateStyles]].</div>",
callerName, item
))
end
return table.concat(res)
end
local function formatInvokeCallList(templateName, invokeList)
local res = {}
for _, item in ipairs(invokeList) do
table.insert(res, string.format(
"<div class='seealso'>'''%s''' invokes function '''%s''' in [[%s]] using [[mw:Special:MyLanguage/Extension:Scribunto|Lua]].</div>",
templateName, item.funcName, item.moduleName
))
end
return table.concat(res)
end
local function formatInvokedByList(moduleName, invokedByList)
local items = {}
for _, invoke in ipairs(invokedByList) do
table.insert(items, string.format("function '''%s''' is invoked by [[%s]]", invoke.funcName, invoke.templateName))
end
table.sort(items)
local res = {}
if #items > COLLAPSE_LIST_LENGTH_THRESHOLD then
table.insert(res, string.format(
"<div class='seealso'>'''%s''' is invoked by %s.</div>",
moduleName, collapseList(items, 'invokedBy', 'templates')[1]
))
else
for _, item in ipairs(items) do
table.insert(res, string.format(
"<div class='seealso'>'''%s's''' %s.</div>",
moduleName, item
))
end
end
return table.concat(res)
end
local function formatRequiredByList(moduleName, requiredByLists)
local requiredByList = formatModuleLinks(requiredByLists.require)
local loadedByList = formatModuleLinks(requiredByLists.loadData)
if #requiredByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
requiredByList = collapseList(requiredByList, 'requiredBy', 'modules')
end
if #loadedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
loadedByList = collapseList(loadedByList, 'loadedBy', 'modules')
end
local res = {}
for _, name in ipairs(requiredByList) do
table.insert(res, string.format("<div class='seealso'>'''%s''' is required by %s.</div>", moduleName, name))
end
for _, name in ipairs(loadedByList) do
table.insert(res, string.format("<div class='seealso'>'''%s''' is loaded by %s.</div>", moduleName, name))
end
return table.concat(res)
end
local function formatImportList(currentPageName, moduleList, id, message)
moduleList = formatModuleLinks(moduleList)
if #moduleList > COLLAPSE_LIST_LENGTH_THRESHOLD then
moduleList = collapseList(moduleList, id, 'modules')
end
local res = arr.map(moduleList, function(moduleName)
return '<div class="seealso">' .. string.format(message, currentPageName, moduleName) .. '</div>'
end)
return table.concat(res)
end
local function formatUsedTemplatesList(currentPageName, usedTemplateList)
usedTemplateList = formatTemplateLinks(usedTemplateList)
local res = {}
if #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then
usedTemplateList = collapseList(usedTemplateList, 'usedTemplates', 'templates')
end
for _, templateName in ipairs(usedTemplateList) do
table.insert(res, string.format(
"<div class='seealso'>'''%s''' transcludes %s using <samp>frame:preprocess()</samp> or <samp>frame:expandTemplate()</samp>.</div>",
currentPageName, templateName
))
end
return table.concat(res)
end
local function messageBoxUnused()
local html = mw.html.create('table'):addClass('messagebox obsolete plainlinks')
html:tag('td')
:attr('width', '40px')
:wikitext('[[File:WikimediaUI-Alert.svg|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.')
:done()
:done()
return tostring(html)
end
-- ============================================================
-- Main entry points
-- ============================================================
local function templateDependencyList(currentPageName, addCategories)
local dependencyList = getTemplateDependencyList(currentPageName)
local res = arr{}
res:insert(formatInvokeCallList(currentPageName, dependencyList.invokeList))
res:insert(formatTemplateStylesList(currentPageName, dependencyList.templateStylesList))
if addCategories then
if #dependencyList.templateStylesList > 0 then
res:insert('[[Category:Templates using TemplateStyles]]')
end
if #dependencyList.invokeList > 0 then
res:insert('[[Category:Lua-based templates]]')
end
end
return table.concat(res)
end
local function moduleDependencyList(currentPageName, addCategories, isUsed)
local moduleContent = mw.title.new(currentPageName):getContent()
assert(moduleContent, string.format('Failed to retrieve text content of page "%s"', currentPageName))
moduleContent = moduleContent:gsub('%-%-%[(=-)%[.-%]%1%]', ''):gsub('%-%-[^\n]*', '')
local requireLists = getRequireLists(moduleContent)
local usedTemplateList = getUsedTemplatesList(moduleContent)
local requiredByLists = getRequiredByLists(currentPageName)
local invokedByList = getInvokedByList(currentPageName)
local res = arr{}
res:insert(formatInvokedByList(currentPageName, invokedByList))
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.usedTemplateList))
res:insert(formatTemplateStylesList(currentPageName, usedTemplateList.templateStylesList, true))
res:insert(formatRequiredByList(currentPageName, requiredByLists))
if addCategories then
res:insert(requireLists.extraCategories, true)
if #usedTemplateList.templateStylesList > 0 then res:insert('[[Category:Modules using TemplateStyles]]') end
if #requireLists.require > 0 then res:insert('[[Category:Modules requiring modules]]') end
if #requireLists.loadData > 0 then 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 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 res:insert('[[Category:Unused modules]]') end
end
return table.concat(res)
end
function p.main(frame)
local args = frame:getParent().args
return p._main(args[1], args.category, args.isUsed)
end
function p._main(currentPageName, addCategories, isUsed)
libraryUtil.checkType('Module:DependencyList._main', 1, currentPageName, 'string', true)
libraryUtil.checkTypeMulti('Module:DependencyList._main', 2, addCategories, {'boolean', 'string', 'nil'})
libraryUtil.checkTypeMulti('Module:DependencyList._main', 3, isUsed, {'boolean', 'string', 'nil'})
local title = mw.title.getCurrentTitle()
if param.is_empty(currentPageName) and
not arr.contains({NS_MODULE_NAME, NS_TEMPLATE_NAME}, title.nsText) then
return ''
end
currentPageName = param.default_to(currentPageName, title.fullText)
currentPageName = currentPageName:gsub('/[Dd]oc$', '')
currentPageName = formatPageName(currentPageName)
if addCategories == nil then
addCategories = title.subpageText ~= 'doc'
end
addCategories = yn(addCategories)
dpl = require('Module:DPLlua')
if currentPageName:find('^' .. NS_TEMPLATE_NAME .. ':') then
return templateDependencyList(currentPageName, addCategories)
end
return moduleDependencyList(currentPageName, addCategories, isUsed)
end
return p
-- </nowiki>