satelito

Static [web] site (or page) generator (ssg) made with Lua script.
git clone git://soucy.cc/satelito.git
Log | Files | Refs | README

commit 2c0fd5eccb29c9aa9d1c502eb050fe35deaae519
parent 375f7b1a871f379a04d013e8f04d4c246fe8c4e1
Author: Hugo Soucy <hugo@soucy.cc>
Date:   Sun,  4 Apr 2021 15:45:07 -0400

Refactor to increase speed of the build process

Diffstat:
Msatelito-dev-1.rockspec | 1+
Msatelito/assets.lua | 5++++-
Msatelito/feed.lua | 1-
Msatelito/file.lua | 38+++++++++++++++++++++++++++++++++++++-
Msatelito/init.lua | 61++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msatelito/list.lua | 2+-
Msatelito/model.lua | 99++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msatelito/page.lua | 18++++++++++++++++++
Msatelito/template.lua | 83++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
9 files changed, 233 insertions(+), 75 deletions(-)

diff --git a/satelito-dev-1.rockspec b/satelito-dev-1.rockspec @@ -14,6 +14,7 @@ dependencies = { --- "argparse >= 0.7.1-1", "etlua >= 0.6.0", + "md5 >= 1.3-1", "mimetypes >= 1.0.0-2", "lua-discount >= 1.2.10.1-1", "luafilesystem >= 1.8.0-1", diff --git a/satelito/assets.lua b/satelito/assets.lua @@ -11,9 +11,12 @@ function assets.export(filepath) local config = assert(site.set_config(filepath), "Sorry, the site configuration can't be set") local mtypes = assert(config.mimetypes) local siblings = assert(lume.array(lfs.dir(file.get_dirname(filepath)))) + local function is_in_mimetypes(f) + return lume.any(mtypes, function(mtype) return mtype == mimetypes.guess(f) end) + end for _, sibling in ipairs(siblings) do - if lume.any(mtypes, function(mtype) return mtype == mimetypes.guess(sibling) end) then + if is_in_mimetypes(sibling) then local content_dir = site.get_root(filepath) .. '/' .. config.paths.content local public_dir = site.get_root(filepath) .. '/' .. config.paths.public_html local src_dir = file.get_dirname(filepath) diff --git a/satelito/feed.lua b/satelito/feed.lua @@ -4,7 +4,6 @@ local feed = {} local lume = require 'satelito.lib.lume.lume' local file = require 'satelito.file' local list = require 'satelito.list' -local model = require 'satelito.model' local site = require 'satelito.site' local template = require 'satelito.template' diff --git a/satelito/file.lua b/satelito/file.lua @@ -4,11 +4,11 @@ local file = {} local lfs = require 'lfs' local mimetypes = require 'mimetypes' local lume = require 'satelito.lib.lume.lume' +local dirtree = require 'satelito.dirtree' -- Check if a file exists function file.exists(filepath) local _file = io.open(filepath, 'r') - local is_file = _file ~= nil and true or false if _file ~= nil then _file:close() @@ -58,6 +58,26 @@ function file.get_relpath(filepath, dirname) return filepath:sub((dirname):len() + 1) end +function file.get_rellink(filepath, dirname) + if file.is_index(filepath) then + return file.get_dirname(file.get_relpath(filepath, dirname)) + else + return '/' .. file.get_relpath(filepath, dirname):match('(.+)%..*') .. '.html' + end +end + +function file.get_permalink(filepath, dirname, url) + return url .. '/' .. file.get_relpath(filepath, dirname):match('(.+)%..*') .. '.html' +end + +function file.get_exportlink(filepath, dirname, htmlpath) + return htmlpath .. file.get_relpath(filepath, dirname):match('(.+)%..*') .. '.html' +end + +function file.get_metafile(filepath) + return dofile(filepath:match('(.+)%..*') .. '.lua') +end + -- Get the modified file from a list of paths function file.get_lastmodified(filelist) return math.max(table.unpack(lume.map(filelist, @@ -77,6 +97,22 @@ function file.is_index(filepath) return (file.get_basename(filepath) == 'index.md') end +-- +function file.get_collection(filepath, dirname) + local collection = {} + + for subfilepath in dirtree.get(file.get_dirname(filepath)) do + if subfilepath + and file.is_markdown(subfilepath) + and not file.is_index(subfilepath) + then + collection[#collection+1] = file.get_relpath(subfilepath, dirname) + end + end + + return collection +end + -- Create a dirtree function file.mkdir(filepath) local sep, pStr = package.config:sub(1, 1), '' diff --git a/satelito/init.lua b/satelito/init.lua @@ -15,8 +15,6 @@ local parser = argparse() :name 'satelito' :description 'Satelito is a static site generator in lua script.' :epilog 'For more info, see https://soucy.cc/git/satelito/file/README.md.html' -local init -local make local args parser:mutex( @@ -25,8 +23,9 @@ parser:mutex( ) parser:flag('-e --export', 'Export the outputed HTML in the *paths.public_html* folder.') -init = parser:command('init', 'Init the sample website in your $HOME folder.') -make = parser:command('make', 'Explicitly process the content in site mode instead of the default page mode.') +local init = parser:command('init', 'Init the sample website in your $HOME folder.') +local make = parser:command('make', 'Explicitly process the content in site mode instead of the default page mode.') +local test = parser:command('test', 'Test some features') args = parser:parse() @@ -90,13 +89,18 @@ end if args['make'] then local config - local content + local contentdir if file.exists('config.lua') then -- Add the currentdir to the package.path package.path = package.path .. ';'.. lfs.currentdir() ..'/?.lua' + + local counter = 0 + local timestart = os.time() + -- Set config.lua in a table config = require 'config' + -- Absolute path to the content directory contentdir = lfs.currentdir() .. '/' .. config.paths.content @@ -106,6 +110,8 @@ if args['make'] then local html, html_path = page.build(filepath, filemeta) local feed_xml, feed_xml_path + counter = counter+1 + if args['export'] then page.export(html_path, html) @@ -121,6 +127,51 @@ if args['make'] then end end end + + print('Satelito builded ' .. counter .. ' HTML pages in ' .. os.difftime(os.time(), timestart) .. ' seconds.') + else + print('There is no "config.lua" here.') + os.exit() + end +end + +if args['test'] then + local config + + if file.exists('config.lua') then + -- Add the currentdir to the package.path + package.path = package.path .. ';'.. lfs.currentdir() ..'/?.lua' + + local timestart = os.time() + local sitemap = {} + + -- Set config.lua in a table + config = require 'config' + + -- Absolute path to the 'content/' directory + local contentdir = lfs.currentdir() .. '/' .. config.paths.content + + -- Array of templates + local templates = lume.array(dirtree.get(lfs.currentdir() .. '/' .. config.paths.templates)) + + for filepath in dirtree.get(contentdir) do + if file.is_markdown(filepath) then + local meta = model.set(filepath, config, contentdir) + + sitemap[meta.relpath] = lume.extend({}, meta) + end + end + + for _, v in pairs(sitemap) do + v.site = sitemap + local html, html_path = page.make(v, templates) + + page.export(html_path, html) + end + + --print(inspect(sitemap)) + + print('Satelito builded in ' .. os.difftime(os.time(), timestart) .. ' second(s).') else print('There is no "config.lua" here.') os.exit() diff --git a/satelito/list.lua b/satelito/list.lua @@ -23,7 +23,7 @@ function list.build(filepath) local child_content = { content = assert(markdown(file.read(child))) } local child_parameters = assert(model.get(child)) - table.insert(_list, lume.extend({}, child_content, child_parameters)) + _list[#_list+1] = lume.extend({}, child_content, child_parameters) end end end diff --git a/satelito/model.lua b/satelito/model.lua @@ -7,53 +7,90 @@ local dirtree = require 'satelito.dirtree' local file = require 'satelito.file' local site = require 'satelito.site' local inspect = require 'inspect' +local markdown = require 'discount' -- lua-discount + +function model.set(filepath, config, contentdir) + local pagemeta = file.get_metafile(filepath) or {} + local time_created = (pagemeta.date..pagemeta.datetime):gsub('%W','') + + -- If required properties are nil + if pagemeta.title == nil then + pagemeta.title = file.get_basename(filepath):match('(.+)%..*') + end + + if pagemeta.date == nil then + pagemeta.date = os.date('%Y-%m-%d', lfs.attributes(filepath).change) + end + + if pagemeta.datetime == nil then + pagemeta.datetime = os.date('%H:%M:%S', lfs.attributes(filepath).change) + end + -- + + -- Path properties + pagemeta.relpath = file.get_relpath(filepath, contentdir) + + -- Link properties + pagemeta.rellink = file.get_rellink(filepath, contentdir) + pagemeta.permalink = file.get_permalink(filepath, contentdir, config.url) + pagemeta.exportlink = file.get_exportlink(filepath, contentdir, config.paths.public_html) + pagemeta.dirlink = file.get_permalink(filepath, contentdir, config.url):match("(.*/)") + + -- Time properties + pagemeta.time_created = time_created + pagemeta.time_modification = lfs.attributes(filepath).modification + pagemeta.time_modified_child = file.get_lastmodified(lume.array(dirtree.get(file.get_dirname(filepath)))) + + -- Unique page ID for the Atom feed + pagemeta.id = 'tag:' .. config.domainname .. ',' .. pagemeta.date .. ':' .. pagemeta.rellink + + -- HTML content + pagemeta.content = markdown(file.read(filepath)) + + -- List + if file.is_index(filepath) and pagemeta.list ~= false then + pagemeta.list = file.get_collection(filepath, contentdir) + end + + -- Templates + pagemeta.template = pagemeta.template or pagemeta.posttype or 'default' + pagemeta.layout = pagemeta.layout or 'layout' + pagemeta.head = pagemeta.head or 'head' + pagemeta.nav = pagemeta.nav or 'navigation' + + return lume.extend({}, pagemeta, config) +end function model.get(filepath, siteconfig) -- The site config.lua file local config = (siteconfig or site.set_config(filepath)) - -- The metadata file of a filepath - local parameters_file = lfs.attributes(filepath:match('(.+)%..*') .. '.lua') - and dofile(filepath:match('(.+)%..*') .. '.lua') - or {} + local contentdir = assert(site.get_root(filepath) .. '/' .. config.paths.content) + local metafile = file.get_metafile(filepath) + local rellink = file.get_rellink(filepath, contentdir) + local id = assert('tag:' .. config.domainname .. ',' .. metafile.date .. ':' .. rellink) -- If there's no parameter file for the markdown -- Then set the minimum from the markdown file itself - if next(parameters_file) == nil then + if next(metafile) == nil then print(filepath:match('(.+)%..*') .. '.lua' .. ' is missing.') print('You should create it to be able to add metadata.') - parameters_file['title'] = file.get_basename(filepath):match('(.+)%..*') - parameters_file['date'] = os.date('%Y-%m-%d', lfs.attributes(filepath).change) - parameters_file['datetime'] = os.date('%H:%M:%S', lfs.attributes(filepath).change) + metafile['title'] = file.get_basename(filepath):match('(.+)%..*') + metafile['date'] = os.date('%Y-%m-%d', lfs.attributes(filepath).change) + metafile['datetime'] = os.date('%H:%M:%S', lfs.attributes(filepath).change) end - local contentdir = site.get_root(filepath) .. '/' .. config.paths.content - local relpath = assert(file.get_relpath(filepath, contentdir)) - local rellink = assert( - file.is_index(filepath) - and file.get_dirname(file.get_relpath(filepath, contentdir)) - or '/' .. relpath:match('(.+)%..*') .. '.html' - ) - local permalink = assert(config.url .. '/' .. relpath:match('(.+)%..*') .. '.html') - local directory = assert(filepath:match("(.*/)")) - local time_modified_child = assert(file.get_lastmodified(lume.array(dirtree.get(directory)))) - local id = assert('tag:' .. config.domainname .. ',' .. parameters_file.date .. ':' .. rellink) return lume.extend({}, config, - parameters_file, - {directory = directory}, - {directory = directory}, - {path = filepath}, - {relpath = relpath}, - {rellink = rellink}, - {permalink = permalink}, - {dirlink = permalink:match("(.*/)")}, - {time_created = (parameters_file.date..parameters_file.datetime):gsub('%W','')}, - {time_updated = lfs.attributes(filepath).modification}, - {time_change = lfs.attributes(filepath).change}, + metafile, + {relpath = file.get_relpath(filepath, contentdir)}, + {rellink = file.get_rellink(filepath, contentdir)}, + {permalink = file.get_permalink(filepath, contentdir, config.url)}, + {dirlink = file.get_permalink(filepath, contentdir, config.url):match("(.*/)")}, + {time_created = (metafile.date..metafile.datetime):gsub('%W','')}, {time_modification = lfs.attributes(filepath).modification}, - {time_modified_child = time_modified_child}, + {time_modified_child = file.get_lastmodified(lume.array(dirtree.get(file.get_dirname(filepath))))}, {id = id} ) end diff --git a/satelito/page.lua b/satelito/page.lua @@ -1,6 +1,7 @@ local page = {} -- local markdown = require 'discount' -- lua-discount +local etlua = require 'etlua' local lume = require 'satelito.lib.lume.lume' local file = require 'satelito.file' local list = require 'satelito.list' @@ -55,6 +56,23 @@ function page.build(filepath, filemeta) end end +function page.make(filemeta, templates) + local post = etlua.compile(file.read(template.find(templates, filemeta.template))) + local head = etlua.compile(file.read(template.find(templates, filemeta.head))) + local navigation = etlua.compile(file.read(template.find(templates, filemeta.nav))) + local layout = etlua.compile(file.read(template.find(templates, filemeta.layout))) + local html = layout( + lume.extend({}, + {post = post(lume.extend({}, filemeta))}, + {head = head(lume.extend({}, filemeta))}, + {navigation = navigation(lume.extend({}, filemeta))} + ) + ) + local html_path = filemeta.exportlink + + return html, html_path +end + --- Export an HTML file to a specific location -- @name page.export -- @param htmlpath a pathname to the HTML file diff --git a/satelito/template.lua b/satelito/template.lua @@ -6,64 +6,77 @@ local lume = require 'satelito.lib.lume.lume' local dirtree = require 'satelito.dirtree' local file = require 'satelito.file' local site = require 'satelito.site' +local inspect = require 'inspect' -- Find the path to a template file from his name local function find_template(templatedir, templatename) - return lume.match( - -- Make an array with all template files - lume.array(dirtree.get(templatedir)), - function(tpl) - -- Try to find a match - return string.gsub(tpl, '(.*/)(.*)', '%2') == templatename .. '.html' - end - ) + return lume.match( + -- Make an array with all template files + lume.array(dirtree.get(templatedir)), + function(tpl) + -- Try to find a match + return string.gsub(tpl, '(.*/)(.*)', '%2') == templatename .. '.html' + end + ) +end + +function template.find(templates, templatename) + local _template = lume.match( + templates, + function(tpl) + -- Try to find a match + return string.gsub(tpl, '(.*/)(.*)', '%2') == templatename .. '.html' + end + ) + + return _template end function template.set_post(parameters, filepath) - local config = assert(site.set_config(filepath)) - local post_template = assert(parameters.posttype and parameters.posttype or parameters.template or 'default') - local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) - local template_file = assert( - find_template(templates_dir, post_template) or find_template(templates_dir, 'default'), - 'The template of this type of post (' .. post_template .. '.html) is missing!' - ) + local config = assert(site.set_config(filepath)) + local post_template = assert(parameters.posttype and parameters.posttype or parameters.template or 'default') + local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) + local template_file = assert( + find_template(templates_dir, post_template) or find_template(templates_dir, 'default'), + 'The template of this type of post (' .. post_template .. '.html) is missing!' + ) - return etlua.compile(file.read(template_file)) + return etlua.compile(file.read(template_file)) end function template.set_layout(parameters, filepath) - local config = assert(site.set_config(filepath)) - local layout = assert(parameters.layout and parameters.layout or 'layout') - local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) - local template_file = assert(find_template(templates_dir, layout), 'The "layout.html" template is missing!') + local config = assert(site.set_config(filepath)) + local layout = assert(parameters.layout and parameters.layout or 'layout') + local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) + local template_file = assert(find_template(templates_dir, layout), 'The "layout.html" template is missing!') - return etlua.compile(file.read(template_file)) + return etlua.compile(file.read(template_file)) end function template.set_head(filepath) - local config = assert(site.set_config(filepath)) - local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) - local template_file = assert(find_template(templates_dir, 'head'), 'The "head.html" template is missing!') + local config = assert(site.set_config(filepath)) + local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) + local template_file = assert(find_template(templates_dir, 'head'), 'The "head.html" template is missing!') - return etlua.compile(file.read(template_file)) + return etlua.compile(file.read(template_file)) end function template.set_navigation(filepath) - local config = assert(site.set_config(filepath)) - local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) - local template_file = assert( - find_template(templates_dir, 'navigation'), 'The "navigation.html" template is missing!' - ) + local config = assert(site.set_config(filepath)) + local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) + local template_file = assert( + find_template(templates_dir, 'navigation'), 'The "navigation.html" template is missing!' + ) - return etlua.compile(file.read(template_file)) + return etlua.compile(file.read(template_file)) end function template.set_feed(filepath) - local config = assert(site.set_config(filepath)) - local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) - local template_file = assert(find_template(templates_dir, 'feed.xml'), 'The "feed.xml" template is missing!') + local config = assert(site.set_config(filepath)) + local templates_dir = assert(site.get_root(filepath) .. '/' .. config.paths.templates) + local template_file = assert(find_template(templates_dir, 'feed.xml'), 'The "feed.xml" template is missing!') - return etlua.compile(file.read(template_file)) + return etlua.compile(file.read(template_file)) end return template