Documentation for this module may be created at Module:MarkdownToWikitext/doc
-- =================================================================
-- Module:Markdown
-- Description: Converts a string of Markdown text into wikitext.
-- Author: Gemini
-- License: CC BY-SA 4.0
-- =================================================================
local p = {}
-- Helper function to trim whitespace from a string
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
-- =================================================================
-- Core Conversion Functions
-- =================================================================
-- Function to strip any existing HTML tags from the input to prevent
-- conflicts and potential security issues. This version is more robust.
local function stripHtml(text)
-- This pattern is more specific and safer than a simple greedy match.
-- It looks for what looks like a valid tag name after the '<'.
text = text:gsub('<%/?%w+[^>]*>', '')
-- A second pass to remove HTML comments, which the above pattern won't catch.
text = text:gsub('<!%-%-.-%-%->', '')
return text
end
-- Converts Markdown-style headers to wikitext headers.
-- This function is designed to work on a single line of text.
-- e.g., ## Header 2 -> == Header 2 ==
local function convertHeaders(text)
for i = 6, 1, -1 do
local h_md = string.rep('#', i)
local h_wiki = string.rep('=', i)
-- The pattern uses '^' to ensure it only matches at the start of the string (line).
text = text:gsub('^' .. h_md .. '%s+(.-)%s*$', h_wiki .. ' %1 ' .. h_wiki)
end
return text
end
-- Converts bold and italic syntax.
-- Handles nested cases by doing bold first.
local function convertEmphasis(text)
-- Bold: **text** or __text__ -> '''text'''
-- The pattern '[^%*]+' is used to handle multiple bold sections on one line correctly.
text = text:gsub('%*%*([^%*]+)%*%*', "'''%1'''")
text = text:gsub('__([^_]+)__', "'''%1'''")
-- Italic: *text* or _text_ -> ''text''
text = text:gsub('%*([^%*]+)%*', "''%1''")
text = text:gsub('_([^_]+)_', "''%1''")
return text
end
-- Converts Markdown links and images.
-- e.g., [text](url) -> [url text]
-- e.g.,  -> [[File:url|alt]]
local function convertLinksAndImages(text)
-- Images must be processed first, as they are a superset of the link syntax.
-- Pattern: !%[(.-)%]%((.-)%)
-- Captures alt text and URL for images.
text = text:gsub('!%[([^\n%]]*)%]%((.-)%)', '[[File:%2|%1]]')
-- Links
-- Pattern: %[(.-)%]%((.-)%)
-- Captures link text and URL.
text = text:gsub('%[([^\n%]]+)%]%((.-)%)', '[%2 %1]')
return text
end
-- Converts inline code blocks.
-- e.g., `code` -> <code>code</code>
local function convertInlineCode(text)
-- Pattern: `(.-)`
-- Lazily captures text between backticks.
return text:gsub('`([^`\n]+)`', '<code>%1</code>')
end
-- =================================================================
-- Main Public Function
-- =================================================================
---
-- The main function to be called from wikitext.
-- It takes a string of Markdown text and converts it to wikitext.
--
-- Usage in wikitext:
-- {{#invoke:Markdown|markdown|source=...}}
-- or when used with ExternalData:
-- {{#invoke:Markdown|markdown|1={{{1}}}}}
---
function p.markdown(frame)
-- Get the input text. It can be passed as the first argument (e.g., from ExternalData)
-- or from a named 'source' argument.
local inputText = frame.args[1] or frame.args.source or ''
-- 1. First, strip any HTML-like tags from the entire block of text.
local text = stripHtml(inputText)
local processedLines = {}
-- 2. Process the text line by line
for line in text:gmatch("([^\n]*)\n?") do
local currentLine = trim(line)
-- Apply transformations. The order of these operations is important.
-- A. Block Transformations (Headers, Lists)
currentLine = convertHeaders(currentLine)
-- List conversion is handled directly here.
currentLine = currentLine:gsub('^%s*[%*%-]%s+', '* ') -- Unordered lists
currentLine = currentLine:gsub('^%s*%d+%.%s+', '# ') -- Ordered lists
-- B. Inline Transformations (Emphasis, Links, Code)
currentLine = convertEmphasis(currentLine)
currentLine = convertLinksAndImages(currentLine)
currentLine = convertInlineCode(currentLine)
table.insert(processedLines, currentLine)
end
-- 3. Re-assemble the processed lines into a single string.
local wikitext = table.concat(processedLines, '\n')
-- 4. Handle paragraph breaks for proper wikitext rendering.
-- Replace two or more consecutive newlines with just two (a single blank line).
wikitext = wikitext:gsub('\n\n+', '\n\n')
return wikitext
end
return p