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

local _M = {};

function _M.clean(title)
    local string = mw.ustring;
    title = string.gsub( title, '^%s*%[*%[%[(.-)%]%]%]*%s*$', function(l)
        return string.gsub( l, '^([^|]*)|.*$', '%1', 1 )
    end, 1 )
        :gsub( '[%s_]+', ' ' )
        :gsub( '/+', '/' )
        :gsub( '^%s', '', 1 )
        :gsub( '%s$', '', 1 )
        :gsub( '/$', '', 1 );
    if title == '' then
        return tostring( mw.title.getCurrentTitle() );
    elseif string.sub( title, 1, 1) == '/' then
        return table.concat{ tostring( mw.title.getCurrentTitle() ), title };
        return title;

-- subpages = require('Module:Page').subpages
-- for page in subpages('Page', [{options}]) do ... end

function _M.subpages(title, options )
    local title, options, pattern = _M.clean(title), options or {};
    pattern, title = pcall(, title, 0);
    if not pattern or not title then
        return ipairs({});
    elseif not options.ignoreNS and not[title.namespace].hasSubpages then
        return ipairs({});
    options.ignoreNS = nil;
    pattern = table.concat{ string.rep( '.', string.len( title.text ) ), '/', '([^\r\n]+)\r?\n' };
    title = title.prefixedText;
    local frame, expand, decode = mw:getCurrentFrame(), mw.text.unstrip, mw.text.decode;
    local function get(name, options, pattern )
        local params = {};
        for k,v in pairs(options) do
            table.insert( params, table.concat{ k, '=', v ~= nil and tostring(v) or '' } );
        params = table.concat( params, '|' );
        if params ~= "" then params = table.concat{ '|', params }; end

Due to a change in Scribunto for Lua, expand() will no longer work on special tags,
including <gallery>, <ref> or other costly extension tags (except "#tag:") or special parser
functions which are converted to UNIQ markers starting and ending by an ASCII DEL character ('\127').

mw.text.unstrip() now ONLY expands and unstrips "<nowiki>" tags, and completely kills ALL others
markers without expanding them to their generated HTML!

The actual full expansion of stripped tags can occur only in MediaWiki after return from Lua.

For this reason we only get an empty list of subpages, and we can no longer bypass the costly
expansion limits of "#ifexist:" or costly calls to by using the Special:prefixindex
parser function...

It is also not possible to use frame:callParserFunction() to process "Special:*" parser functions,
even if they are converted to "#tag:Special*" in Mediawiki, they are stripped in MediaWiki,
but they not found by Lua's frame:callParserFunction().

As well frame:extensionTag() cannot be used and generate the same error in Lua (tag "special" not found).
        params = expand( frame:preprocess( string.format('{{special:prefixindex/%s/%s}}', name, params) ) );
        params = string.gsub( params, '%b<>', function(tag) return tag == '</a>' and '\n' or ''; end );
        options = {}
        for k in string.gmatch( params, pattern ) do table.insert( options, decode(k) ); end
        return options;
    local subpages = get( title, options, pattern );
    return function(title, last)
        local page;
        while true do
            page = table.remove( subpages, 1 );
            if page ~= nil then return page, last; end
            options.from = table.concat{ title, '/', last };
            subpages = get( title, options, pattern );
            table.remove( subpages, 1 );
            if #subpages == 0 then
                return nil;
    end, title;

return _M
Read in another language