Module:WikitextProcessor
Appearance
Documentation for this module may be created at Module:WikitextProcessor/doc
-- Module:WikitextProcessor
-- Processes JSON content with wikitext formatting for frontend display
-- Handles placeholder replacement, wiki link conversion to HTML, and content normalization
local p = {}
-- Dependencies
local ErrorHandling = require('Module:ErrorHandling')
-- Module-level cache for processed content
local contentCache = {}
--------------------------------------------------------------------------------
-- Caching Mechanism (copied from TemplateHelpers to avoid circular dependency)
--------------------------------------------------------------------------------
-- Helper for generating cache keys from multiple arguments
-- @param prefix String prefix for the cache key (usually the function name)
-- @param ... Any number of arguments to include in the cache key
-- @return A string cache key
local function generateCacheKey(prefix, ...)
local args = {...}
local parts = {prefix}
for i, arg in ipairs(args) do
if type(arg) == "table" then
-- For tables, we can't reliably generate a cache key
-- So we just use a placeholder with the table's memory address
parts[i+1] = "table:" .. tostring(arg)
elseif type(arg) == "nil" then
parts[i+1] = "nil"
else
parts[i+1] = tostring(arg)
end
end
return table.concat(parts, ":")
end
-- Generic caching wrapper
-- @param cacheKey The cache key to use
-- @param operation A function that returns the value to cache
-- @return The cached result or the result of executing the operation
local function withCache(cacheKey, operation)
-- Check if result is already cached
if contentCache[cacheKey] ~= nil then
return contentCache[cacheKey]
end
-- Execute operation and cache result
local result = operation()
contentCache[cacheKey] = result
return result
end
-- Constants as upvalues for performance
local WIKI_LINK_PATTERNS = {
-- Pattern 1: [[Page Name]] -> HTML page links
{
pattern = '%[%[([^#|%]]+)%]%]',
processor = function(pageName, errorContext)
local spacesReplaced = (pageName:gsub(' ', '_'))
local success1, pageUrl = pcall(function()
return tostring(mw.uri.fullUrl(spacesReplaced))
end)
if not success1 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'mw.uri.fullUrl failed for page link', 'Page: ' .. pageName .. ', Error: ' .. tostring(pageUrl))
end
return '[[' .. pageName .. ']]'
end
local success2, encodedName = pcall(function()
return mw.text.encode(pageName)
end)
if not success2 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'mw.text.encode failed for page link', 'Page: ' .. pageName .. ', Error: ' .. tostring(encodedName))
end
return '[[' .. pageName .. ']]'
end
local success3, result = pcall(function()
return string.format('<a href="%s">%s</a>', pageUrl, encodedName)
end)
if not success3 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'string.format failed for page link', 'Page: ' .. pageName .. ', Error: ' .. tostring(result))
end
return '[[' .. pageName .. ']]'
end
return result
end
},
-- Pattern 2: [[Page|text]] -> HTML page links with custom text
{
pattern = '%[%[([^#|%]]+)|([^%]]+)%]%]',
processor = function(pageName, text, errorContext)
local success1, pageUrl = pcall(function()
return tostring(mw.uri.fullUrl((pageName:gsub(' ', '_'))))
end)
if not success1 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'Pattern 2 mw.uri.fullUrl failed', 'Error: ' .. tostring(pageUrl))
end
return '[[' .. pageName .. '|' .. text .. ']]'
end
local success2, encodedText = pcall(function()
return mw.text.encode(text)
end)
if not success2 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'Pattern 2 mw.text.encode failed', 'Error: ' .. tostring(encodedText))
end
return '[[' .. pageName .. '|' .. text .. ']]'
end
local success3, result = pcall(function()
return string.format('<a href="%s">%s</a>', pageUrl, encodedText)
end)
if not success3 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'Pattern 2 string.format failed', 'Error: ' .. tostring(result))
end
return '[[' .. pageName .. '|' .. text .. ']]'
end
return result
end
},
-- Pattern 3: [[#anchor|text]] -> HTML anchor links
{
pattern = '%[%[#([^|%]]+)|([^%]]+)%]%]',
processor = function(anchor, text, errorContext)
local success1, encodedAnchor = pcall(function()
return mw.uri.anchorEncode(anchor)
end)
if not success1 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'Pattern 3 mw.uri.anchorEncode failed', 'Error: ' .. tostring(encodedAnchor))
end
return '[[#' .. anchor .. '|' .. text .. ']]'
end
local success2, encodedText = pcall(function()
return mw.text.encode(text)
end)
if not success2 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'Pattern 3 mw.text.encode failed', 'Error: ' .. tostring(encodedText))
end
return '[[#' .. anchor .. '|' .. text .. ']]'
end
local success3, result = pcall(function()
return string.format('<a href="#%s">%s</a>', encodedAnchor, encodedText)
end)
if not success3 then
if errorContext then
ErrorHandling.addStatus(errorContext, 'wikiLinkProcessor', 'Pattern 3 string.format failed', 'Error: ' .. tostring(result))
end
return '[[#' .. anchor .. '|' .. text .. ']]'
end
return result
end
}
}
-- Normalizes content string by cleaning up whitespace
function p.normalizeContentString(content)
if not content or content == "" then
return content
end
local cacheKey = generateCacheKey("normalizeContentString", content)
return withCache(cacheKey, function()
-- Apply string normalization - consolidate whitespace operations
-- Handle multiple return values from gsub properly
local result = (content:gsub("%s+", " "))
result = (result:gsub("^%s+", ""))
result = (result:gsub("%s+$", ""))
return result
end)
end
-- Replaces placeholder patterns ($VARIABLE$) with actual values
function p.replacePlaceholders(content, placeholderMap)
if not content or not placeholderMap then
return content
end
local cacheKey = generateCacheKey("replacePlaceholders", content, tostring(placeholderMap))
return withCache(cacheKey, function()
local result = content
-- Apply placeholder replacement - handle multiple return values
for key, value in pairs(placeholderMap) do
if value and value ~= "" then
result = (result:gsub("%$" .. key .. "%$", value))
end
end
-- Clean up any remaining unfilled placeholders
result = (result:gsub("%$[A-Z_]+%$", ""))
-- Final normalization after placeholder replacement
result = (result:gsub("%s+", " "))
result = (result:gsub("^%s+", ""))
result = (result:gsub("%s+$", ""))
return result
end)
end
-- Processes wiki links in content and converts them to HTML
function p.processWikiLinksToHTML(content, errorContext)
if not content or content == "" then
return content
end
local cacheKey = generateCacheKey("processWikiLinksToHTML", content)
return withCache(cacheKey, function()
local result = content
-- Process each wiki link pattern in sequence
for _, patternInfo in ipairs(WIKI_LINK_PATTERNS) do
-- Handle multiple return values from gsub properly
result = (result:gsub(patternInfo.pattern, function(...)
return patternInfo.processor(..., errorContext)
end))
end
return result
end)
end
-- Main entry point: Processes JSON content with wikitext formatting for frontend display
-- @param content The content string to process
-- @param placeholders Optional table of placeholder values for $VARIABLE$ replacement
-- @param errorContext Optional error context for error reporting
-- @return Processed content ready for frontend display
function p.processContentForFrontend(content, placeholders, errorContext)
if not content or content == "" then
return content
end
-- Create cache key including all parameters
local placeholdersKey = placeholders and tostring(placeholders) or "nil"
local cacheKey = generateCacheKey("processContentForFrontend", content, placeholdersKey)
return withCache(cacheKey, function()
-- Step 1: Normalize content string
local processedContent = p.normalizeContentString(content)
-- Step 2: Replace placeholders if provided
if placeholders then
processedContent = p.replacePlaceholders(processedContent, placeholders)
end
-- Step 3: Process wiki links to HTML
processedContent = p.processWikiLinksToHTML(processedContent, errorContext)
return processedContent
end)
end
return p