Actions

Module

Module:Changelog

From Project Rebearth

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

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",
}

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

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

local function getRelatedPages(entry)
    local aliases = mw.loadData("Module:Aliases/data")
    local pages = {}
    local seen = {}

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

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

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

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

    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

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

local function renderEntry(frame, entry)
    local line = "* " .. validateTag(entry.tag) .. " " .. entry.text
    return frame:preprocess(line)
end

function p.all(frame)
    local _dataTitle = mw.title.new("Module:Changelog/data.json")
    local data = mw.text.jsonDecode(_dataTitle:getContent())
    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(frame, entry)
        end
    end

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

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 _dataTitle = mw.title.new("Module:Changelog/data.json")
    local data = mw.text.jsonDecode(_dataTitle:getContent())

    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

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

    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(frame, 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 table.concat(out, "\n")
end

return p