Module:DependencyList: Difference between revisions

osrsw>CephHunter
Module:Enum -> Module:Array
osrsw>CephHunter
Add support for loadJsonData
Line 1: Line 1:
-- <nowiki>
local p = {}
local p = {}
local libraryUtil = require( 'libraryUtil' )
local libraryUtil = require( 'libraryUtil' )
Line 11: Line 10:
local MAX_DYNAMIC_REQUIRE_LIST_LENGTH = 30
local MAX_DYNAMIC_REQUIRE_LIST_LENGTH = 30
local dynamicRequireListQueryCache = {}
local dynamicRequireListQueryCache = {}
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 = { "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'.
Line 44: Line 54:
local function formatPageName( str )
local function formatPageName( str )
     local name = mw.text.trim(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( '^([\'\"])(.-)%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 )
Line 53: Line 63:


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


Line 66: Line 87:
local function dualGmatch( str, pat1, pat2 )
local function dualGmatch( str, pat1, pat2 )
     local f1 = string.gmatch( str, pat1 )
     local f1 = string.gmatch( str, pat1 )
     local f2 = string.gmatch( str, pat2 )
     if pat2 then
    return function()
        local f2 = string.gmatch( str, pat2 )
        return f1() or f2()
        return function()
            return f1() or f2()
        end
    else
        return f1
     end
     end
end
local function isDynamicPath( str )
    return string.find( str, '%.%.' ) or string.find( str, '%%%a' )
end
end


Line 75: Line 104:
--- 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
---@param query string
---@return string[]     Sequence of strings
---@return string[]
local function getDynamicRequireList( query )
local function getDynamicRequireList( query )
     if query:find( '%.%.' ) then
     if query:find( '%.%.' ) then
Line 83: Line 112:
         query = table.concat( query )
         query = table.concat( query )
     else
     else
         _, query = query:match( '(["\'])(.-)%1' )
         local _, _query = query:match( '(["\'])(.-)%1' )
         query = query:gsub( '%%%a', '%%' )
         query = _query:gsub( '%%%a', '%%' )
     end
     end
     query = query:gsub( '^[Mm]odule:', '' )
     query = query:gsub( '^[Mm]odule:', '' )
Line 120: Line 149:
--- Returns a list of modules loaded and required by module 'moduleName'.
--- Returns a list of modules loaded and required by module 'moduleName'.
---@param moduleName string
---@param moduleName string
---@param searchForUsedTemplates boolean
---@param searchForUsedTemplates? boolean
---@return string[], string[], string[]
---@return table<string, string[]>
local function getRequireList( moduleName, searchForUsedTemplates )
local function getRequireList( moduleName, searchForUsedTemplates )
     local content = mw.title.new( moduleName ):getContent()
     local content = mw.title.new( moduleName ):getContent()
     local requireList = arr{}
     local requireList = arr{}
     local loadDataList = arr{}
     local loadDataList = arr{}
    local loadJsonDataList = arr{}
     local usedTemplateList = arr{}
     local usedTemplateList = arr{}
     local dynamicRequirelist = arr{}
     local dynamicRequirelist = arr{}
     local dynamicLoadDataList = arr{}
     local dynamicLoadDataList = arr{}
    local dynamicLoadJsonDataList = arr{}
    local extraCategories = arr{}


     assert( content ~= nil, string.format( '%s does not exist', moduleName ) )
     assert( content ~= nil, string.format( '%s does not exist', moduleName ) )
Line 134: Line 166:
     content = content:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments
     content = content:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments


     for match in dualGmatch( content, 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do
     local function getList( pat1, pat2, list, dynList )
        match = mw.text.trim( match )
        for match in dualGmatch( content, pat1, pat2 ) do
        match = extractModuleName( match, content )
            match = mw.text.trim( match )
            local name = extractModuleName( match, content )


        if match:find( '%.%.' ) or match:find( '%%%a' ) then
            if isDynamicPath( name ) then
            for _, x in ipairs( getDynamicRequireList( match ) ) do
                dynList:insert( getDynamicRequireList( name ), true )
                table.insert( dynamicRequirelist, x )
             elseif name ~= '' then
             end
                name = formatModuleName( name, true )
        elseif match ~= '' then
                table.insert( list, name )
            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
                if builtins[name] then
        match = mw.text.trim( match )
                    extraCategories = extraCategories:insert( builtins[name].categories, true )
        match = extractModuleName( match, content )
                 end
 
        if match:find( '%.%.' ) or match:find( '%%%a' ) then
            for _, x in ipairs( getDynamicRequireList( match ) ) do
                 table.insert( dynamicLoadDataList, x )
             end
             end
        elseif match ~= '' then
            match = formatModuleName( match )
            table.insert( loadDataList, match )
         end
         end
     end
     end


     for func, match in string.gmatch( content, 'pcall%s*%(([^,]+),([^%),]+)' ) do
     getList( 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)', requireList, dynamicRequirelist )
        func = mw.text.trim( func )
    getList( 'mw%.loadData%s*(%b())', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)', loadDataList, dynamicLoadDataList )
        match = mw.text.trim( match )
    getList( 'mw%.loadJsonData%s*(%b())', 'mw%.loadJsonData%s*((["\'])%s*[Mm]odule:.-%2)', loadJsonDataList, dynamicLoadJsonDataList )
 
    getList( 'pcall%s*%(%s*require%s*,([^%),]+)', nil, requireList, dynamicRequirelist )
        if func == 'require' then
    getList( 'pcall%s*%(%s*mw%.loadData%s*,([^%),]+)', nil, loadDataList, dynamicLoadDataList )
            for _, x in ipairs( getDynamicRequireList( match ) ) do
    getList( 'pcall%s*%(%s*mw%.loadJsonData%s*,([^%),]+)', nil, loadJsonDataList, dynamicLoadJsonDataList )
                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
     if searchForUsedTemplates then
Line 218: Line 232:
     end
     end


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


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


Line 243: Line 267:
         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} )
Line 271: Line 295:
     html:tag( 'td' )
     html:tag( 'td' )
         :attr( 'width', '40xp' )
         :attr( 'width', '40xp' )
         :wikitext( '[[File:Willow logs (historical).png|center|30px|link=]]' )
         :wikitext( '[[File:Iron full helm detail old.png|center|30px|link=]]' )
     :done()
     :done()
     :tag( 'td' )
     :tag( 'td' )
Line 353: Line 377:
---@return string
---@return string
local function formatInvokedByList( moduleName, addCategories, whatLinksHere )
local function formatInvokedByList( moduleName, addCategories, whatLinksHere )
local function lcfirst( str )
    local function lcfirst( str )
return string.gsub( str, '^[Mm]odule:.', string.lower )
        return string.gsub( str, '^[Mm]odule:.', string.lower )
end
    end


     local templateData = arr.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end )
     local templateData = arr.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end )
Line 406: Line 430:
local function formatRequiredByList( moduleName, addCategories, whatLinksHere )
local function formatRequiredByList( moduleName, addCategories, whatLinksHere )
     local childModuleData = arr.map( whatLinksHere, function ( title )
     local childModuleData = arr.map( whatLinksHere, function ( title )
         local requireList, loadDataList = getRequireList( title )
         local lists = getRequireList( title )
         return {name=title, requireList=requireList, loadDataList=loadDataList}
         return {name=title, requireList=lists.requireList, loadDataList=lists.loadDataList .. lists.loadJsonDataList}
     end )
     end )


Line 471: Line 495:
end
end


local function formatRequireList( currentPageName, addCategories, requireList )
local function formatImportList( currentPageName, moduleList, id, message, category )
    local res = {}
     if #moduleList > COLLAPSE_LIST_LENGTH_THRESHOLD then
 
         moduleList = collapseList( moduleList, id, 'modules' )
     if #requireList > COLLAPSE_LIST_LENGTH_THRESHOLD then
         requireList = collapseList( requireList, 'require', 'modules' )
     end
     end


     for _, requiredModuleName in ipairs( requireList ) do
     local res = arr.map( moduleList, function( moduleName )
         table.insert( res, string.format(
         return '<div class="seealso">' .. string.format( message, currentPageName, moduleName ) .. '</div>'
            "<div class='seealso'>'''%s''' requires %s.</div>",
     end )
            currentPageName,
            requiredModuleName
        ) )
    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
        table.insert( res, string.format(
            "<div class='seealso'>'''%s''' loads data from %s.</div>",
            currentPageName,
            loadedModuleName
        ) )
     end


     if #loadDataList > 0 then
     if #moduleList > 0 and category then
         table.insert( res, (addCategories and '[[Category:Modules using data]]' or '') )
         table.insert( res, string.format( '[[Category:%s]]', category ) )
     end
     end


Line 563: Line 559:


     if title.text:lower():find( 'sandbox' ) then
     if title.text:lower():find( 'sandbox' ) then
    moduleIsUsed = true -- Don't show sandbox modules as unused
        moduleIsUsed = true -- Don't show sandbox modules as unused
     end
     end


     if currentPageName:find( '^Template:' ) or currentPageName:find( '^Calculator:' ) then
     if currentPageName:find( '^Template:' ) or currentPageName:find( '^Calculator:' ) then
         local invokeList = getInvokeCallList( currentPageName )
         local invokeList = getInvokeCallList( currentPageName )
         return formatInvokeCallList( currentPageName, addCategories, invokeList )
         return formatInvokeCallList(currentPageName, addCategories, invokeList)
     end
     end


Line 590: Line 586:
     } )
     } )


     local requireList, loadDataList, usedTemplateList = getRequireList( currentPageName, true )
     local lists = getRequireList( currentPageName, true )


     requireList = arr.map( requireList, function ( moduleName )
     local requireList = arr.map( lists.requireList, function ( moduleName )
         if moduleName:find( '%%' ) then
         if moduleName:find( '%%' ) then
             return formatDynamicQueryLink( moduleName )
             return formatDynamicQueryLink( moduleName )
        elseif builtins[moduleName] then
            return '[[' .. builtins[moduleName].link .. '|' .. moduleName .. ']]'
         else
         else
             return '[[' .. moduleName .. ']]'
             return '[[' .. moduleName .. ']]'
Line 600: Line 598:
     end )
     end )


     loadDataList = arr.map( loadDataList, function ( moduleName )
     local loadDataList = arr.map( lists.loadDataList, function ( moduleName )
         if moduleName:find( '%%' ) then
         if moduleName:find( '%%' ) then
             return formatDynamicQueryLink( moduleName )
             return formatDynamicQueryLink( moduleName )
Line 608: Line 606:
     end )
     end )


     usedTemplateList = arr.map( usedTemplateList, function( templateName )
     local loadJsonDataList = arr.map( lists.loadJsonDataList, function ( moduleName )
        if moduleName:find( '%%' ) then
            return formatDynamicQueryLink( moduleName )
        else
            return '[[' .. moduleName .. ']]'
        end
    end )
 
    local usedTemplateList = arr.map( lists.usedTemplateList, function( templateName )
         if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
         if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
             return '[['..templateName..']]'
             return '[['..templateName..']]'
Line 619: Line 625:


     table.insert( res, formatInvokedByList( currentPageName, addCategories, whatTemplatesLinkHere ) )
     table.insert( res, formatInvokedByList( currentPageName, addCategories, whatTemplatesLinkHere ) )
     table.insert( res, formatRequireList( currentPageName, addCategories, requireList ) )
     table.insert( res, formatImportList( currentPageName, requireList, 'require', "'''%s''' requires %s.", addCategories and 'Modules requiring modules' ) )
     table.insert( res, formatLoadDataList( currentPageName, addCategories, loadDataList ) )
    table.insert( res, formatImportList( currentPageName, loadDataList, 'loadData', "'''%s''' loads data from %s.", addCategories and 'Modules using data' ) )
     table.insert( res, formatImportList( currentPageName, loadJsonDataList, 'loadJsonData', "'''%s''' loads data from %s.", addCategories and 'Modules using data' ) )
     table.insert( res, formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList ) )
     table.insert( res, formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList ) )
     table.insert( res, formatRequiredByList( currentPageName, addCategories, whatModulesLinkHere ) )
     table.insert( res, formatRequiredByList( currentPageName, addCategories, whatModulesLinkHere ) )
    if addCategories then
        local extraCategories = arr.map( lists.extraCategories, function( categoryName )
            return "[[Category:" .. categoryName .. "]]"
        end )
        table.insert( res, table.concat( extraCategories ) )
    end


     if not moduleIsUsed then
     if not moduleIsUsed then
Line 632: Line 647:


return p
return p
-- </nowiki>