Actions

Module

Module:Changelog

From Project Rebearth

Revision as of 19:18, 26 February 2026 by East6 (talk | contribs) (Created page with "-- Module:Changelog -- Renders changelog entries from Module:Changelog/data. -- Public: p.all(frame), p.forPage(frame) local p = {} local VALID_TAGS = { New = true, Balance = true, Fix = true, QoL = true, Performance = true, } local MONTH_NAMES = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", } --- Format "YYYY-MM-DD" → "February 14, 2026" local function formatD...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:Changelog/doc

-- Module:Changelog
-- Renders changelog entries from Module:Changelog/data.
-- Public: p.all(frame), p.forPage(frame)

local p = {}

local VALID_TAGS = {
    New = true,
    Balance = true,
    Fix = true,
    QoL = true,
    Performance = true,
}

local MONTH_NAMES = {
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December",
}

--- Format "YYYY-MM-DD" → "February 14, 2026"
local function formatDate(isoDate)
    local y, m, d = isoDate:match("^(%d+)-(%d+)-(%d+)$")
    if not y then return isoDate end
    local month = MONTH_NAMES[tonumber(m)]
    if not month then return isoDate end
    return month .. " " .. tonumber(d) .. ", " .. y
end

--- Validate tag; return tag name or error span.
local function validateTag(tag)
    if VALID_TAGS[tag] then
        return "{{Tag|" .. tag .. "}}"
    end
    return '<span class="error">Unknown tag: "' .. tag .. '"</span>'
end

--- Extract page names that an entry relates to.
--- Scans wikilinks [[Page]] / [[Page|display]], Icon templates {{Icon|Res}},
--- resolves aliases, merges manual pages, and removes excludes.
local function getRelatedPages(entry)
    local aliases = mw.loadData("Module:Aliases/data")
    local pages = {}
    local seen = {}

    local function addPage(name)
        -- Resolve alias
        local canonical = aliases[name] or name
        if not seen[canonical] then
            seen[canonical] = true
            pages[#pages + 1] = canonical
        end
    end

    -- Scan [[PageName]] and [[PageName|display]]
    for target in entry.text:gmatch("%[%[([^%]|]+)[^%]]*%]%]") do
        local trimmed = mw.text.trim(target)
        if trimmed ~= "" then
            addPage(trimmed)
        end
    end

    -- Scan {{Icon|ResourceName}}
    for resource in entry.text:gmatch("{{Icon|([^}]+)}}") do
        local trimmed = mw.text.trim(resource)
        if trimmed ~= "" then
            addPage(trimmed)
        end
    end

    -- Merge manual pages
    if entry.pages then
        for _, name in ipairs(entry.pages) do
            addPage(name)
        end
    end

    -- Remove excludes
    if entry.exclude then
        local excludeSet = {}
        for _, name in ipairs(entry.exclude) do
            excludeSet[name] = true
        end
        local filtered = {}
        for _, name in ipairs(pages) do
            if not excludeSet[name] then
                filtered[#filtered + 1] = name
            end
        end
        pages = filtered
    end

    return pages
end

--- Group entries by date, preserving input order.
local function groupByDate(entries)
    local groups = {}
    local dateOrder = {}
    local dateMap = {}

    for _, entry in ipairs(entries) do
        if not dateMap[entry.date] then
            dateMap[entry.date] = {}
            dateOrder[#dateOrder + 1] = entry.date
        end
        local group = dateMap[entry.date]
        group[#group + 1] = entry
    end

    for _, date in ipairs(dateOrder) do
        groups[#groups + 1] = { date = date, entries = dateMap[date] }
    end

    return groups
end

--- Render a single entry as a wikitext bullet.
local function renderEntry(entry)
    return "* " .. validateTag(entry.tag) .. " " .. entry.text
end

--- p.all — Full changelog for the main page.
function p.all(frame)
    local data = mw.loadData("Module:Changelog/data")
    local groups = groupByDate(data)
    local out = {}

    for _, group in ipairs(groups) do
        out[#out + 1] = "=== " .. formatDate(group.date) .. " ==="
        for _, entry in ipairs(group.entries) do
            out[#out + 1] = renderEntry(entry)
        end
    end

    return frame:preprocess(table.concat(out, "\n"))
end

--- p.forPage — Per-page filtered changelog.
function p.forPage(frame)
    local args = frame.args
    local targetPage = args.page or args[1]
    if not targetPage or targetPage == "" then
        targetPage = mw.title.getCurrentTitle().text
    end

    local limit = tonumber(args.limit) or 5

    local data = mw.loadData("Module:Changelog/data")

    -- Filter entries relevant to targetPage
    local matched = {}
    for _, entry in ipairs(data) do
        local relatedPages = getRelatedPages(entry)
        for _, pageName in ipairs(relatedPages) do
            if pageName == targetPage then
                matched[#matched + 1] = entry
                break
            end
        end
    end

    -- Empty state
    if #matched == 0 then
        return '<div class="rb-changelog rb-changelog-empty">No changelog entries for this page yet.</div>'
    end

    -- Limit entries
    local limited = {}
    for i = 1, math.min(limit, #matched) do
        limited[#limited + 1] = matched[i]
    end

    local groups = groupByDate(limited)
    local out = {}

    out[#out + 1] = '<div class="rb-changelog">'
    for _, group in ipairs(groups) do
        out[#out + 1] = "==== " .. formatDate(group.date) .. " ===="
        for _, entry in ipairs(group.entries) do
            out[#out + 1] = renderEntry(entry)
        end
    end

    out[#out + 1] = '<div class="rb-changelog-more">'
    out[#out + 1] = "''[[Project Rebearth#Changelogs|See full changelog →]]''"
    out[#out + 1] = "</div>"
    out[#out + 1] = "</div>"

    return frame:preprocess(table.concat(out, "\n"))
end

return p