Toggle search
Search
Toggle menu
Notifications
Toggle personal menu
Editing
Module:DependencyList
From WoopMC
Views
Read
Edit
View history
associated-pages
Module
Discussion
More actions
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
-- <nowiki> local p = {} local libraryUtil = require( 'libraryUtil' ) local arr = require( 'Module:Array' ) local yn = require( 'Module:Yesno' ) local param = require( 'Module:Paramtest' ) local dpl = require( 'Module:DPLlua' ) local mHatnote = require('Module:Hatnote') local mHatlist = require('Module:Hatnote list') local moduleIsUsed = false local COLLAPSE_LIST_LENGTH_THRESHOLD = 1 local MAX_DYNAMIC_REQUIRE_LIST_LENGTH = 30 local dynamicRequireListQueryCache = {} --- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'. ---@param content string The content of the module to search in ---@param varName string ---@return string local function substVarValue( content, varName ) local res = content:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or content:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or '' if res:find( '^(["\'])[Mm]odule:[%S]+%1' ) and not res:find( '%.%.' ) and not res:find( '%%%a' ) then return mw.text.trim( res ) else return '' end end ---@param capture string ---@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 return capture elseif capture:find( '^[%a_][%w_]*$' ) then -- Check if if is a single variable return substVarValue( content, capture ) end return capture end ---@param str string ---@return string local function formatPageName( str ) local name = mw.text.trim(str) :gsub( '^([\'\"])(.-)%1$', function(_, x) return x end ) -- Only remove quotes at start and end of string if both are the same type :gsub( '_', ' ' ) :gsub( '^.', string.upper ) :gsub( ':.', string.upper ) return name end ---@param str string ---@return string local function formatModuleName( str ) local module = formatPageName( str ) if not string.find( module, '^[Mm]odule:' ) then module = 'Module:' .. module end return module end local function dualGmatch( str, pat1, pat2 ) local f1 = string.gmatch( str, pat1 ) local f2 = string.gmatch( str, pat2 ) return function() return f1() or f2() end end --- Used in case a construct like 'require( "Module:wowee/" .. isTheBest )' is found. --- Will return a list of pages which satisfy this pattern where 'isTheBest' can take any value. ---@param query string ---@return string[] Sequence of strings local function getDynamicRequireList( query ) if query:find( '%.%.' ) then query = mw.text.split( query, '..', true ) query = arr.map( query, function(x) return mw.text.trim(x) end ) query = arr.map( query, function(x) return (x:match('^[\'\"](.-)[\'\"]$') or '%') end ) query = table.concat( query ) else _, query = query:match( '(["\'])(.-)%1' ) query = query:gsub( '%%%a', '%%' ) end query = query:gsub( '^[Mm]odule:', '' ) if dynamicRequireListQueryCache[ query ] then return dynamicRequireListQueryCache[ query ] end local list = dpl.ask{ namespace = 'Module', titlematch = query, nottitlematch = '%/doc|'..query..'/%', distinct = 'strict', ignorecase = true, ordermethod = 'title', count = MAX_DYNAMIC_REQUIRE_LIST_LENGTH + 1, skipthispage = 'no', allowcachedresults = true, cacheperiod = 604800 -- One week } if #list > MAX_DYNAMIC_REQUIRE_LIST_LENGTH then list = { 'Module:' .. query } end dynamicRequireListQueryCache[ query ] = list return list end --- Returns a list of modules loaded and required by module 'moduleName'. ---@param moduleName string ---@param searchForUsedTemplates boolean ---@return string[], string[], string[] local function getRequireList( moduleName, searchForUsedTemplates ) local content = mw.title.new( moduleName ):getContent() local requireList = arr{} local loadDataList = arr{} local usedTemplateList = arr{} local dynamicRequirelist = arr{} local dynamicLoadDataList = arr{} assert( content ~= nil, string.format( '%s does not exist', moduleName ) ) content = content:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments for match in dualGmatch( content, 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do match = mw.text.trim( match ) match = extractModuleName( match, content ) if match:find( '%.%.' ) or match:find( '%%%a' ) then for _, x in ipairs( getDynamicRequireList( match ) ) do table.insert( dynamicRequirelist, x ) end elseif match ~= '' then match = formatModuleName( match ) table.insert( requireList, match ) end end for match in dualGmatch( content, 'mw%.loadData%s*(%b())', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)' ) do match = mw.text.trim( match ) match = extractModuleName( match, content ) if match:find( '%.%.' ) or match:find( '%%%a' ) then for _, x in ipairs( getDynamicRequireList( match ) ) do table.insert( dynamicLoadDataList, x ) end elseif match ~= '' then match = formatModuleName( match ) table.insert( loadDataList, match ) end end for func, match in string.gmatch( content, 'pcall%s*%(([^,]+),([^%),]+)' ) do func = mw.text.trim( func ) match = mw.text.trim( match ) if func == 'require' then for _, x in ipairs( getDynamicRequireList( match ) ) do table.insert( dynamicRequirelist, x ) end elseif func == 'mw.loadData' then for _, x in ipairs( getDynamicRequireList( match ) ) do table.insert( dynamicLoadDataList, x ) end end end if searchForUsedTemplates then for preprocess in string.gmatch( content, ':preprocess%s*(%b())' ) do local function recursiveGMatch( str, pat ) local list = {} local i = 0 repeat for match in string.gmatch( list[i] or str, pat ) do table.insert( list, match ) end i = i + 1 until i > #list or i > 100 i = 0 return function() i = i + 1 return list[i] end end for template in recursiveGMatch( preprocess, '{(%b{})}' ) do local name = string.match( template, '{(.-)[|{}]' ) if name ~= '' then if name:find( ':' ) then local ns = name:match( '^(.-):' ) if arr.contains( {'', 'template', 'user'}, ns:lower() ) then table.insert( usedTemplateList, name ) elseif ns == ns:upper() then table.insert( usedTemplateList, ns ) -- Probably a magic word end else if name:match( '^%u+$' ) or name == '!' then table.insert( usedTemplateList, name ) -- Probably a magic word else table.insert( usedTemplateList, 'Template:'..name ) end end end end end end requireList = requireList .. dynamicRequirelist:reject( loadDataList ) requireList = requireList:unique() loadDataList = loadDataList .. dynamicLoadDataList:reject( requireList ) loadDataList = loadDataList:unique() usedTemplateList = usedTemplateList:unique() table.sort( requireList ) table.sort( loadDataList ) table.sort( usedTemplateList ) return requireList, loadDataList, usedTemplateList end --- Returns a list with module and function names used in all '{{#Invoke:moduleName|funcName}}' found on page 'templateName'. ---@param templateName string ---@return table<string, string>[] local function getInvokeCallList( templateName ) local content = mw.title.new( templateName ):getContent() local invokeList = {} assert( content ~= nil, string.format( '%s does not exist', templateName ) ) for moduleName, funcName in string.gmatch( content, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do moduleName = formatModuleName( moduleName ) funcName = mw.text.trim( funcName ) if string.find( funcName, '^{{{' ) then funcName = funcName .. '}}}' end table.insert( invokeList, {moduleName=moduleName, funcName=funcName} ) end invokeList = arr.unique( invokeList, function(x) return x.moduleName..x.funcName end ) table.sort( invokeList, function(x, y) return x.moduleName..x.funcName < y.moduleName..y.funcName end ) return invokeList end --- Returns a list with TemplateStyles found on page 'pageName'. ---@param templateName string ---@return table<string>[] local function getTemplateStylesList( pageName ) local content = mw.title.new( pageName ):getContent() local templateStylesList = {} assert( content ~= nil, string.format( '%s does not exist', pageName ) ) -- Greedy match to match <templatestyles>, {{#tag:templatestyles}}, and lua calls for styleName in string.gmatch( content, 'templatestyles.+src%s?=%s?[\'"](.[^\'"]+%.css)[\'"]' ) do -- TemplateStyles default to Template: when there are no namespace suffix if string.find( styleName, ':', 1, true ) then styleName = mw.text.trim( styleName ) else styleName = string.format( 'Template:%s', mw.text.trim( styleName ) ) end styleName = formatPageName( styleName ) table.insert( templateStylesList, {styleName=styleName} ) end templateStylesList = arr.unique( templateStylesList, function(x) return x.styleName end ) table.sort( templateStylesList, function(x, y) return x.styleName < y.styleName end ) return templateStylesList end ---@param pageName string ---@param addCategories boolean ---@return string local function messageBoxUnused( pageName, addCategories ) local html = mw.html.create( 'div' ):addClass( 'mbox mbox-med mbox-unusedmodule' ):attr( 'role', 'presentation') html:tag( 'span' ) :addClass( 'mbox-title' ) :tag( 'span' ) :addClass( 'mbox-icon metadata' ) :wikitext( '[[File:WikimediaUI-Alert.svg|14px|link=]]' ) :done() :wikitext( 'This module is unused.' ) :done() :tag( 'span' ) :addClass( 'mbox-text' ) :wikitext( string.format( 'This module is neither invoked by a template nor required/loaded by another module. If this is in error, make sure to add <code>{{[[Template:Documentation|Documentation]]}}</code>/<code>{{[[Template:No documentation|No documentation]]}}</code> to the calling template\'s or parent\'s module documentation.', pageName ) ) :wikitext( addCategories and '[[Category:Unused modules]]' or '' ) :done() :done() return tostring( html ) end local function collapseList( list, id, listType ) local text = string.format( '%d %s', #list, listType ) local button = '<span>' .. text .. ':</span> ' local content = mHatlist.andList( list, false ) return { tostring( button ) .. tostring( content ) } end --- Creates a link to [[Special:Search]] showing all pages found by getDynamicRequireList() in case it found more than MAX_DYNAMIC_REQUIRE_LIST_LENGTH pages. ---@param query string @This will be in a format like 'Module:Wowee/%' or 'Module:Wowee/%/data' ---@return string local function formatDynamicQueryLink( query ) local prefix = query:match( '^([^/]+)' ) local linkText = query:gsub( '%%', '< ... >' ) query = query:gsub( '^Module:', '' ) query = query:gsub( '([^/]+)/?', function ( match ) if match == '%' then return '\\/[^\\/]+' else return '\\/"' .. match .. '"' end end ) query = query:gsub( '^\\/', '' ) query = string.format( 'intitle:/%s%s/i -intitle:/%s\\/""/i -intitle:doc prefix:"%s"', query, query:find( '"$' ) and '' or '""', query, prefix ) return string.format( '<span class="plainlinks">[%s %s]</span>', tostring( mw.uri.fullUrl( 'Special:Search', { search = query } ) ), linkText ) end ---@param templateName string ---@param addCategories boolean ---@param invokeList table<string, string>[] @This is the list returned by getInvokeCallList() ---@return string local function formatInvokeCallList( templateName, addCategories, invokeList ) local category = addCategories and '[[Category:Lua-based templates]]' or '' local res = {} for _, item in ipairs( invokeList ) do local msg = string.format( "'''%s''' invokes function '''%s''' in [[%s]] using [[WoopMC:Lua|Lua]].", templateName, item.funcName, item.moduleName ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Code.svg' } ) ) end if #invokeList > 0 then table.insert( res, category ) end return table.concat( res ) end ---@param moduleName string ---@param addCategories boolean ---@param whatLinksHere string @A list generated by a dpl of pages in the Template namespace which link to moduleName. ---@return string local function formatInvokedByList( moduleName, addCategories, whatLinksHere ) local function lcfirst( str ) return string.gsub( str, '^[Mm]odule:.', string.lower ) end local templateData = arr.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end ) templateData = arr.filter( templateData, function(x) return arr.any( x.invokeList, function(y) return lcfirst(y.moduleName) == lcfirst(moduleName) end ) end ) local invokedByList = {} for _, template in ipairs( templateData ) do for _, invoke in ipairs( template.invokeList ) do table.insert( invokedByList, string.format( "function '''%s''' is invoked by [[%s]]", invoke.funcName, template.templateName ) ) end end table.sort( invokedByList) local res = {} if #invokedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then local msg = string.format( "'''%s''' is invoked by %s.", moduleName, collapseList( invokedByList, 'invokedBy', 'templates' )[1] ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Code.svg' } ) ) else for _, item in ipairs( invokedByList ) do local msg = string.format( "'''%s's''' %s.", moduleName, item ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Code.svg' } ) ) end end if #templateData > 0 then moduleIsUsed = true table.insert( res, (addCategories and '[[Category:Template invoked modules]]' or '') ) end return table.concat( res ) end ---@param moduleName string ---@param addCategories boolean ---@param whatLinksHere string @A list generated by a dpl of pages in the Module namespace which link to moduleName. ---@return string local function formatRequiredByList( moduleName, addCategories, whatLinksHere ) local childModuleData = arr.map( whatLinksHere, function ( title ) local requireList, loadDataList = getRequireList( title ) return {name=title, requireList=requireList, loadDataList=loadDataList} end ) local requiredByList = arr.map( childModuleData, function ( item ) if arr.any( item.requireList, function(x) return x:lower()==moduleName:lower() end ) then if item.name:find( '%%' ) then return formatDynamicQueryLink( item.name ) else return '[[' .. item.name .. ']]' end end end ) local loadedByList = arr.map( childModuleData, function ( item ) if arr.any( item.loadDataList, function(x) return x:lower()==moduleName:lower() end ) then if item.name:find( '%%' ) then return formatDynamicQueryLink( item.name ) else return '[[' .. item.name .. ']]' end end end ) if #requiredByList > 0 or #loadedByList > 0 then moduleIsUsed = true end 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 _, requiredByModuleName in ipairs( requiredByList ) do local msg = string.format( "'''%s''' is required by %s.", moduleName, requiredByModuleName ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Code.svg' } ) ) end if #requiredByList > 0 then table.insert( res, (addCategories and '[[Category:Modules required by modules]]' or '') ) end for _, loadedByModuleName in ipairs( loadedByList ) do local msg = string.format( "'''%s''' is loaded by %s.", moduleName, requiredByModuleName ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Code.svg' } ) ) end if #loadedByList > 0 then table.insert( res, (addCategories and '[[Category:Module data]]' or '') ) end return table.concat( res ) end local function formatRequireList( currentPageName, addCategories, requireList ) local res = {} if #requireList > COLLAPSE_LIST_LENGTH_THRESHOLD then requireList = collapseList( requireList, 'require', 'modules' ) end for _, requiredModuleName in ipairs( requireList ) do local msg = string.format( "'''%s''' requires %s.", currentPageName, requiredModuleName ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Code.svg' } ) ) end if #requireList > 0 then table.insert( res, (addCategories and '[[Category:Modules requiring modules]]' or '') ) end return table.concat( res ) end local function formatLoadDataList( currentPageName, addCategories, loadDataList ) local res = {} if #loadDataList > COLLAPSE_LIST_LENGTH_THRESHOLD then loadDataList = collapseList( loadDataList, 'loadData', 'modules' ) end for _, loadedModuleName in ipairs( loadDataList ) do local msg = string.format( "''%s''' loads data from %s.", currentPageName, loadedModuleName ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Code.svg' } ) ) end if #loadDataList > 0 then table.insert( res, (addCategories and '[[Category:Modules using data]]' or '') ) end return table.concat( res ) end local function formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList ) local res = {} if #usedTemplateList > COLLAPSE_LIST_LENGTH_THRESHOLD then usedTemplateList = collapseList( usedTemplateList, 'usedTemplates', 'templates' ) end for _, templateName in ipairs( usedTemplateList ) do local msg = string.format( "'''%s''' transcludes [[%s]] using <samp>frame:preprocess()</samp>.", currentPageName, templateName ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Code.svg' } ) ) end return table.concat( res ) end ---@param templateName string ---@param addCategories boolean ---@param templateStylesList table<string>[] @This is the list returned by getTemplateStylesList() ---@return string local function formatTemplateStylesList( currentPageName, addCategories, templateStylesList ) local category = addCategories and '[[Category:Templates using TemplateStyles]]' or '' local res = {} for _, item in ipairs( templateStylesList ) do local msg = string.format( "'''%s''' invokes [[%s]] using [[WoopMC:TemplateStyles|TemplateStyles]].", currentPageName, item.styleName ) table.insert( res, mHatnote._hatnote( msg, { icon='WikimediaUI-Palette.svg' } ) ) end if #templateStylesList > 0 then table.insert( res, category ) end return table.concat( res ) end function p.main( frame ) local args = frame:getParent().args return p._main( args[1], args.category, args.isUsed ) end ---@param currentPageName string|nil ---@param addCategories boolean|string|nil ---@return string function p._main( currentPageName, addCategories, isUsed ) libraryUtil.checkType( 'Module:RequireList._main', 1, currentPageName, 'string', true ) libraryUtil.checkTypeMulti( 'Module:RequireList._main', 2, addCategories, {'boolean', 'string', 'nil'} ) libraryUtil.checkTypeMulti( 'Module:RequireList._main', 3, isUsed, {'boolean', 'string', 'nil'} ) local title = mw.title.getCurrentTitle() -- Leave early if not in module or template namespace if param.is_empty( currentPageName ) and ( not arr.contains( {'Module', 'Template'}, title.nsText ) ) then return '' end 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 ) ) if title.text:lower():find( 'sandbox' ) then moduleIsUsed = true -- Don't show sandbox modules as unused end local templateStylesList = getTemplateStylesList( currentPageName ) if currentPageName:find( '^Template:' ) then local invokeList = getInvokeCallList( currentPageName ) return formatInvokeCallList( currentPageName, addCategories, invokeList ) .. formatTemplateStylesList( currentPageName, addCategories, templateStylesList ) end local whatTemplatesLinkHere, whatModulesLinkHere = dpl.ask( { namespace = 'Template', linksto = currentPageName, distinct = 'strict', ignorecase = true, ordermethod = 'title', allowcachedresults = true, cacheperiod = 604800 -- One week }, { namespace = 'Module', linksto = currentPageName, nottitlematch = '%/doc%|' .. currentPageName:gsub( 'Module:', '' ), distinct = 'strict', ignorecase = true, ordermethod = 'title', allowcachedresults = true, cacheperiod = 604800 -- One week } ) local requireList, loadDataList, usedTemplateList = getRequireList( currentPageName, true ) requireList = arr.map( requireList, function ( moduleName ) if moduleName:find( '%%' ) then return formatDynamicQueryLink( moduleName ) else return '[[' .. moduleName .. ']]' end end ) loadDataList = arr.map( loadDataList, function ( moduleName ) if moduleName:find( '%%' ) then return formatDynamicQueryLink( moduleName ) else return '[[' .. moduleName .. ']]' end end ) usedTemplateList = arr.map( usedTemplateList, function( templateName ) if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not return '[['..templateName..']]' else return "'''{{"..templateName.."}}'''" -- Magic words don't have a page so make them bold instead end end ) local res = {} table.insert( res, formatInvokedByList( currentPageName, addCategories, whatTemplatesLinkHere ) ) table.insert( res, formatRequireList( currentPageName, addCategories, requireList ) ) table.insert( res, formatLoadDataList( currentPageName, addCategories, loadDataList ) ) table.insert( res, formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList ) ) table.insert( res, formatRequiredByList( currentPageName, addCategories, whatModulesLinkHere ) ) table.insert( res, formatTemplateStylesList( currentPageName, addCategories, templateStylesList ) ) if not moduleIsUsed then table.insert( res, 1, messageBoxUnused( currentPageName:gsub( 'Module:', '' ), addCategories ) ) end return table.concat( res ) end return p -- </nowiki>
Summary:
Please note that all contributions to WoopMC may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, 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 (see
WoopMC:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Template used on this page:
Module:DependencyList/doc
(
edit
)