satelito

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

commit 4800ae897a22d20a7b05b1ea49abc2da979c6a45
parent 5704f9e57b06e7ac9de23159483ad2af3109f192
Author: Hugo Soucy <hugo@soucy.cc>
Date:   Wed, 30 Nov 2022 14:47:23 -0500

Merge branch 'refactor_2022-nov'

Diffstat:
Msatelito/file.lua | 26+++++++++++++++++++++++---
Msatelito/init.lua | 201++++++++++++++++++++++++++++++++-----------------------------------------------
Msatelito/list.lua | 42+++++++++++++++---------------------------
Msatelito/model.lua | 91+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msatelito/site.lua | 85++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msatelito/sitemapxml.lua | 4++--
6 files changed, 224 insertions(+), 225 deletions(-)

diff --git a/satelito/file.lua b/satelito/file.lua @@ -123,13 +123,13 @@ function file.get_rellink(filepath, dirname) end end --- -function file.get_collection(filepath, dirname) +-- Get a list of relative children paths from a filepath +function file.get_list(filepath, dirname) local collection = {} for subfilepath in dirtree.get(file.get_dirname(filepath)) do if subfilepath - and (file.is_markdown(subfilepath) or file.is_html(subfilepath)) + and file.is_content(subfilepath) and not file.is_index(subfilepath) then collection[#collection+1] = file.get_relpath(subfilepath, dirname) @@ -139,6 +139,26 @@ function file.get_collection(filepath, dirname) return collection end +-- Get a table collection of contents +function file.get_collection(collection) + if collection and type(collection) == 'table' then + local collection_list = {} + local contentdir = _G.Satelito.contentdir + + for i = 1, #collection do + if lfs.attributes(contentdir..collection[i]).mode == 'directory' then + collection_list[#collection_list+1] = file.get_list(contentdir..collection[i], contentdir) + else + collection_list[#collection_list+1] = { collection[i] } + end + end + + return lume.concat(table.unpack(collection_list)) + end + + return +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 @@ -8,7 +8,6 @@ local lume = require 'satelito.lib.lume.lume' local model = require 'satelito.model' local dirtree = require 'satelito.dirtree' local file = require 'satelito.file' -local sitemapxml = require 'satelito.sitemapxml' local site = require 'satelito.site' -- local init @@ -36,6 +35,47 @@ exec:argument 'bin name' args = parser:parse() +-- Started +print('=> Satelito is here ...') + +------------- +-- GLOBALS -- +------------- + +-- Set the Satelito global table +_G.Satelito = {} +_G.Satelito.timestart = os.time() + +-- Put config.lua in a table +print('=> Fetching the configuration file content ...') +_G.Satelito.config = dofile(site.get_config(lfs.currentdir()..'/')) + +-- Change current directory for the config.lua's directory +print('=> Moving where the site configuration file is located ...') +lfs.chdir(file.get_dirname(site.get_config(lfs.currentdir()..'/'))) + +-- Then add the current directory to the package.path +package.path = package.path .. ';'.. lfs.currentdir() ..'/?.lua' + +-- Get the absolute path for the 'content' directory +_G.Satelito.contentdir = lfs.currentdir()..'/'.._G.Satelito.config.paths.content + +-- Get the absolute path for the 'public_html' directory +_G.Satelito.publicdir = lfs.currentdir()..'/'.._G.Satelito.config.paths.public_html + +-- Get the list of templates +print('=> Fetching the templates ...') +_G.Satelito.templates = lume.array(dirtree.get(lfs.currentdir() .. '/' .. _G.Satelito.config.paths.templates)) + +-- Get the arguments +_G.Satelito.args = args + +--print(inspect(_G.Satelito)) + +---------- +-- EXEC -- +---------- + if args['exec'] then package.path = package.path .. ';'.. lfs.currentdir() ..'/?.lua' @@ -47,6 +87,11 @@ if args['exec'] then end +---------- +-- INIT -- +---------- + +-- Initialize the satelito sample site in $HOME -- Example '$ satelito init' if args['init'] then os.execute('mkdir ~/satelito-sample') @@ -61,149 +106,65 @@ if args['init'] then return end +---------- +-- PIPE -- +---------- + +-- Pipe stdout into satelito -- Example: '$ find site/content/ | satelito pipe' if args['pipe'] then - local configpath = false - local config = false - local contentdir = false - local sitemap = {} - local templates - local timestart = os.time() - - print('=> Satelito is on ...') + local sitedata = {} for filepath in (io.lines()) do - if file.is_markdown(filepath) or file.is_html(filepath) then - -- Get the site configuration's path - if not configpath then - print('=> Searching for the nearest config.lua ...') - configpath = site.get_config(filepath) - end + if file.is_content(filepath) then + -- Get the pagedata of the file + local pagedata = model.set(filepath) - -- Change the current directory to the config's directory - if lfs.currentdir()..'/' ~= file.get_dirname(configpath) then - print('=> Moving where the configuration file is located ...') - lfs.chdir(file.get_dirname(configpath)) + -- Add the pagedata of the file into the sitedata table + if lume.count(sitedata) == 0 then + print('=> Fetching the markdown and HTML content ...') end - -- If the site configuration file exists - if file.exists(configpath) then - -- Add the currentdir to the package.path - package.path = package.path .. ';'.. lfs.currentdir() ..'/?.lua' - - -- Set config.lua in a table - if not config then - print('=> Fetching the configuration file content ...') - config = require 'config' - end - - -- Absolute path to the 'content/' directory - if not contentdir then - contentdir = lfs.currentdir() .. '/' .. config.paths.content - end - - -- Get the list of templates - if not templates then - print('=> Fetching the templates ...') - templates = lume.array(dirtree.get(lfs.currentdir() .. '/' .. config.paths.templates)) - end - - -- Get the meta of the file - local meta = model.set(filepath, lume.extend(config, {templates = templates}), contentdir) - - -- Collection - -- Get the files of a collection (only for the pipe command) - if meta.collection and type(meta.collection) == 'table' then - for i = 1, #meta.collection do - if lfs.attributes(contentdir..meta.collection[i]).mode == 'directory' then - for collectable in dirtree.get(contentdir..meta.collection[i]) do - if file.is_content(collectable) and file.get_metafile(collectable) then - sitemap[#sitemap+1] = model.set(collectable, lume.extend(config, {templates = templates}), contentdir) - end - end - else - if file.is_content(contentdir..meta.collection[i]) - and file.get_metafile(contentdir..meta.collection[i]) - then - sitemap[#sitemap+1] = model.set(contentdir..meta.collection[i], lume.extend(config, {templates = templates}), contentdir) - end - end - end - end - - -- Add the meta of the file into the sitemap table - if lume.count(sitemap) == 0 then - print('=> Fetching the markdown and HTML content ...') - end - - sitemap[#sitemap+1] = meta - end + sitedata[#sitedata+1] = pagedata end end - print('=> '..lume.count(sitemap)..' content found') - + print('=> '..lume.count(sitedata)..' content found') print('=> Making the web site ...') -- Sorting by alphanum - table.sort(lume.unique(sitemap), function(a, b) return a.idorder > b.idorder end) + table.sort(lume.unique(sitedata), function(a, b) return a.idorder > b.idorder end) - return site.make(sitemap, args['export'], timestart) + return site.make(sitedata) end +---------- +-- MAKE -- +---------- + -- Make command -- Example: '$ satelito make --export' if args['make'] then - local config - local templates - local timestart = os.time() - local sitemap = {} - - print('=> Satelito is on ...') - - if file.exists('config.lua') then - -- Add the currentdir to the package.path - package.path = package.path .. ';'.. lfs.currentdir() ..'/?.lua' - - -- Set config.lua in a table - print('=> Fetching the configuration file content ...') - config = require 'config' - - -- Absolute path to the 'content/' directory - local contentdir = lfs.currentdir() .. '/' .. config.paths.content - - print('=> Fetching the templates ...') - templates = lume.array(dirtree.get(lfs.currentdir() .. '/' .. config.paths.templates)) + local config = _G.Satelito.config; + local contentdir = _G.Satelito.contentdir + local templates = _G.Satelito.templates + local sitedata = {} - print('=> Fetching the markdown and HTML content ...') + print('=> Fetching the markdown and HTML content ...') - for filepath in dirtree.get(contentdir) do - if file.is_markdown(filepath) or file.is_html(filepath) then - local meta = model.set(filepath, lume.extend(config, {templates = templates}), contentdir) + for filepath in dirtree.get(contentdir) do + if file.is_content(filepath) then + local pagedata = model.set(filepath) - sitemap[#sitemap+1] = meta - end + sitedata[#sitedata+1] = pagedata end + end - print('=> '..lume.count(sitemap)..' content found') - print('=> Making the web site ...') - - -- Sort before make the website - table.sort(lume.unique(sitemap), function(a, b) return a.idorder > b.idorder end) - - site.make(sitemap, args['export'], timestart) + print('=> '..lume.count(sitedata)..' content found') + print('=> Making the web site ...') - -- Make and export the sitemap.xml - if config.sitemapxml and args['export'] then - local sitemapxml_xml, sitemapxml_xml_path = sitemapxml.make( - sitemap, templates, config.paths.public_html - ) - file.export(sitemapxml_xml_path, sitemapxml_xml) - end + -- Sort before make the website + table.sort(lume.unique(sitedata), function(a, b) return a.idorder > b.idorder end) - return - else - print('There is no "config.lua" here.') - os.exit() - end + return site.make(sitedata, args['export']) end diff --git a/satelito/list.lua b/satelito/list.lua @@ -3,8 +3,7 @@ local list = {} -- local dirtree = require 'satelito.dirtree' local file = require 'satelito.file' -local inspect = require 'inspect' -local lfs = require 'lfs' -- luafilesystem +local model = require 'satelito.model' local lume = require 'satelito.lib.lume.lume' -- Pagination @@ -29,16 +28,24 @@ function list.set_pagination(pagelist, len) return slicedlist end --- Children -function list.get_children(children_list, sitemap, asc) +-- Get children contents +function list.get_children(children_list, sitedata, asc) local children = {} + local relpaths = {} + local contentdir = _G.Satelito.contentdir if children_list then - for i = 1, #sitemap do - local is_child = lume.find(children_list, sitemap[i].relpath) + for i = 1, #sitedata do + relpaths[#relpaths+1] = sitedata[i].relpath + end + + for i = 1, #children_list do + local is_exists = lume.find(relpaths, children_list[i]) - if is_child then - children[#children+1] = sitemap[i] + if is_exists then + children[#children+1] = sitedata[is_exists] + else + children[#children+1] = model.set(contentdir..children_list[i]) end end @@ -53,25 +60,6 @@ function list.get_children(children_list, sitemap, asc) return children end -function list.get_collection(collection, sitemap, asc) - if collection and type(collection) == 'table' then - local collection_list = {} - local contentdir = lfs.currentdir()..'/'..sitemap[1].paths.content - - for i = 1, #collection do - if lfs.attributes(contentdir..collection[i]).mode == 'directory' then - collection_list[#collection_list+1] = file.get_collection(contentdir..collection[i], contentdir) - else - collection_list[#collection_list+1] = { collection[i] } - end - end - - return list.get_children(lume.concat(table.unpack(collection_list)), sitemap, asc) - end - - return -end - -- Archives function list.get_archives(contentdir) local archives_table = {} diff --git a/satelito/model.lua b/satelito/model.lua @@ -6,90 +6,89 @@ local lfs = require 'lfs' -- luafilesystem local lume = require 'satelito.lib.lume.lume' local dirtree = require 'satelito.dirtree' local file = require 'satelito.file' -local list = require 'satelito.list' local markdown = require 'discount' -- lua-discount local template = require 'satelito.template' -function model.set(filepath, config, contentdir) - local pagemeta = file.get_metafile(filepath) or {} +function model.set(filepath) + local pagedata = file.get_metafile(filepath) or {} + local config = _G.Satelito.config + local contentdir = _G.Satelito.contentdir + local publicdir = _G.Satelito.publicdir + local templates = _G.Satelito.templates local time_created -- If required properties are nil - pagemeta.title = pagemeta.title or file.get_basename(filepath):match('(.+)%..*') - pagemeta.date = pagemeta.date or os.date('%Y-%m-%d', lfs.attributes(filepath).change) - pagemeta.datetime = pagemeta.datetime or os.date('%H:%M:%S', lfs.attributes(filepath).change) + pagedata.title = pagedata.title or file.get_basename(filepath):match('(.+)%..*') + pagedata.date = pagedata.date or os.date('%Y-%m-%d', lfs.attributes(filepath).change) + pagedata.datetime = pagedata.datetime or os.date('%H:%M:%S', lfs.attributes(filepath).change) -- Path properties - pagemeta.contentdir = contentdir - pagemeta.path = filepath - pagemeta.relpath = file.get_relpath(filepath, contentdir) - pagemeta.reldir = pagemeta.relpath:match("(.*/)") + pagedata.contentdir = contentdir + pagedata.path = filepath + pagedata.relpath = file.get_relpath(filepath, contentdir) + pagedata.reldir = pagedata.relpath:match("(.*/)") -- Link properties - pagemeta.rellink = file.get_rellink(filepath, contentdir) - pagemeta.rellinkdir = '/'.. (pagemeta.reldir ~= nil and pagemeta.reldir or '') - pagemeta.permalink = file.get_permalink(filepath, contentdir, config.siteurl) - pagemeta.exportlink = file.get_exportlink(filepath, contentdir, config.paths.public_html) - pagemeta.dirlink = file.get_permalink(filepath, contentdir, config.siteurl):match("(.*/)") + pagedata.rellink = file.get_rellink(filepath, contentdir) + pagedata.rellinkdir = '/'.. (pagedata.reldir ~= nil and pagedata.reldir or '') + pagedata.permalink = file.get_permalink(filepath, contentdir, config.siteurl) + pagedata.exportlink = file.get_exportlink(filepath, contentdir, publicdir) + pagedata.dirlink = file.get_permalink(filepath, contentdir, config.siteurl):match("(.*/)") -- Time properties - time_created = (pagemeta.date..pagemeta.datetime):gsub('%W','') + time_created = (pagedata.date..pagedata.datetime):gsub('%W','') - 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)))) + pagedata.time_created = time_created + pagedata.time_modification = lfs.attributes(filepath).modification + pagedata.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.siteurl:match('^%w+://([^/]+)') .. ',' .. pagemeta.date .. ':' .. pagemeta.rellink - pagemeta.idorder = pagemeta.rellinkdir .. time_created + pagedata.id = 'tag:' .. config.siteurl:match('^%w+://([^/]+)') .. ',' .. pagedata.date .. ':' .. pagedata.rellink + pagedata.idorder = pagedata.rellinkdir .. time_created -- HTML content if file.is_markdown(filepath) then - pagemeta.content = markdown(file.read(filepath)) + pagedata.content = markdown(file.read(filepath)) elseif file.is_html(filepath) then - pagemeta.content = file.read(filepath) + pagedata.content = file.read(filepath) end -- Summary - if pagemeta.summary then - pagemeta.summary = markdown(pagemeta.summary) + if pagedata.summary then + pagedata.summary = markdown(pagedata.summary) end -- List (and Feed) - if file.is_index(filepath) and pagemeta.list ~= false then - pagemeta.list = file.get_collection(filepath, contentdir) + if file.is_index(filepath) and pagedata.list ~= false then + pagedata.list = file.get_list(filepath, contentdir) end - -- Archives - if pagemeta.archives then - pagemeta.archives_children = list.get_archives(contentdir) - end - - -- Tags - if pagemeta.tags then - pagemeta.tags_children = list.get_tags(contentdir) + -- Collection + -- File list + if pagedata.collection then + pagedata.collection_list = file.get_collection(pagedata.collection) end -- Change the language for a specific content - pagemeta.language = pagemeta.language or config.language + pagedata.language = pagedata.language or config.language -- Templates - pagemeta.template = pagemeta.template or pagemeta.posttype or 'default' - pagemeta.layout = pagemeta.layout or 'layout' - pagemeta.head = pagemeta.head or 'head' - pagemeta.navigation = pagemeta.navigation or 'navigation' - pagemeta.footer = pagemeta.footer or 'footer' - pagemeta.feed = pagemeta.feed or 'feed.xml' + pagedata.template = pagedata.template or pagedata.posttype or 'default' + pagedata.layout = pagedata.layout or 'layout' + pagedata.head = pagedata.head or 'head' + pagedata.navigation = pagedata.navigation or 'navigation' + pagedata.footer = pagedata.footer or 'footer' + pagedata.feed = pagedata.feed or 'feed.xml' -- Include a partial template and compile it -- @usage <%- include("test-partial") %> - pagemeta.include = function(templatename) - local inc = etlua.compile(file.read(template.find(config.templates, templatename))) + pagedata.include = function(templatename) + local inc = etlua.compile(file.read(template.find(templates, templatename))) - return inc(lume.extend({}, config, pagemeta)) + return inc(lume.extend({}, config, pagedata)) end - return lume.extend({}, config, pagemeta) + return lume.extend({}, config, pagedata, {templates = templates}) end return model diff --git a/satelito/site.lua b/satelito/site.lua @@ -10,6 +10,7 @@ local file = require 'satelito.file' local list = require 'satelito.list' local lume = require 'satelito.lib.lume.lume' local page = require 'satelito.page' +local sitemapxml = require 'satelito.sitemapxml' --- From a filepath get the closest 'config.lua' by climbing the -- directory tree @@ -29,58 +30,79 @@ function site.get_config(filepath) return site.get_config(dir_parent) end --- Make the site (from the sitemap) -function site.make(sitemap, export, timestart) +-- Make the site (from the sitedata) +function site.make(sitedata) local duration - - for i = 1, #sitemap do + local config = _G.Satelito.config + local contentdir = _G.Satelito.contentdir + local export = _G.Satelito.args['export'] + local make = _G.Satelito.args['make'] + local publicdir = _G.Satelito.publicdir + local templates = _G.Satelito.templates + local timestart = _G.Satelito.timestart + + for i = 1, #sitedata do local html, html_path local feed_xml, feed_xml_path local paginated - sitemap[i].index = i + sitedata[i].index = i + + -- Lists + -- Subpages from the actual index page + if sitedata[i].list ~= false and file.is_index(sitedata[i].path) then + sitedata[i].children = list.get_children(sitedata[i].list, sitedata, sitedata[i].asc) + end + + -- Collections + -- Group of pages from specific relpaths + if sitedata[i].collection and sitedata[i].collection_list then + sitedata[i].collection = list.get_children(sitedata[i].collection_list, sitedata, sitedata[i].asc) + end - if sitemap[i].list ~= false and file.is_index(sitemap[i].path) then - sitemap[i].children = list.get_children(sitemap[i].list, sitemap, sitemap[i].asc) + -- Archives + if sitedata[i].archives then + sitedata[i].archives_children = list.get_archives(contentdir) end - if sitemap[i].collection then - sitemap[i].collection = list.get_collection(sitemap[i].collection, sitemap, sitemap[i].asc) + -- Tags + if sitedata[i].tags then + sitedata[i].tags_children = list.get_tags(contentdir) end if i > 1 then - sitemap[i].relprev = sitemap[i-1] + sitedata[i].relprev = sitedata[i-1] end - if i < #sitemap then - sitemap[i].relnext = sitemap[i+1] + if i < #sitedata then + sitedata[i].relnext = sitedata[i+1] end -- If a pagination is requested - if sitemap[i].pagination and file.is_index(sitemap[i].relpath) then + if sitedata[i].pagination and file.is_index(sitedata[i].relpath) then print('=> A pagination is requested ...') - sitemap[i].pagination.limit = sitemap[i].pagination.limit or 6 - sitemap[i].pagination.prefix = sitemap[i].pagination.prefix or 'pg-' + sitedata[i].pagination.limit = sitedata[i].pagination.limit or 6 + sitedata[i].pagination.prefix = sitedata[i].pagination.prefix or 'pg-' -- Split page children by pagination limit - paginated = list.set_pagination(sitemap[i].collection or sitemap[i].children, sitemap[i].pagination.limit) + paginated = list.set_pagination(sitedata[i].collection or sitedata[i].children, sitedata[i].pagination.limit) -- Set the pagination length - sitemap[i].pagination.length = #paginated + sitedata[i].pagination.length = #paginated print('=> Making the pagination pages ...') for p = 1, #paginated do - sitemap[i].children = paginated[p] - sitemap[i].pagination.current = p + sitedata[i].children = paginated[p] + sitedata[i].pagination.current = p -- Make the pagination pages if p == 1 then - html, html_path = page.make(sitemap[i]) + html, html_path = page.make(sitedata[i]) else html, html_path = page.make( - sitemap[i], - sitemap[i].exportlink:match("(.*/)")..sitemap[i].pagination.prefix..p..'.html' + sitedata[i], + sitedata[i].exportlink:match("(.*/)")..sitedata[i].pagination.prefix..p..'.html' ) end -- Export the pagination pages @@ -93,7 +115,7 @@ function site.make(sitemap, export, timestart) else -- Make the page - html, html_path = page.make(sitemap[i]) + html, html_path = page.make(sitedata[i]) -- Export the page if export then @@ -104,23 +126,32 @@ function site.make(sitemap, export, timestart) end -- Feed - if file.is_index(sitemap[i].relpath) and export then - feed_xml, feed_xml_path = feed.make(sitemap[i]) + if file.is_index(sitedata[i].relpath) and export then + feed_xml, feed_xml_path = feed.make(sitedata[i]) file.export(feed_xml_path, feed_xml) end -- Copy assets to the public_html/ folder if export then - assets.export(sitemap[i]) + assets.export(sitedata[i]) end end + -- Make and export the sitemap.xml + if config.sitemapxml and make and export then + local sitemapxml_xml, sitemapxml_xml_path = sitemapxml.make( + sitedata, templates, publicdir + ) + + file.export(sitemapxml_xml_path, sitemapxml_xml) + end + duration = os.difftime(os.time(), timestart) > 0 and 'in approximately '..os.difftime(os.time(), timestart)..' second(s).' or 'in less than 1 second.' print('--------------------------------------------------------------------------') - print('Satelito built '..lume.count(sitemap)..' HTML page(s) '..duration) + print('Satelito built '..lume.count(sitedata)..' HTML page(s) '..duration) end return site diff --git a/satelito/sitemapxml.lua b/satelito/sitemapxml.lua @@ -5,9 +5,9 @@ local etlua = require 'etlua' local file = require 'satelito.file' local template = require 'satelito.template' -function sitemapxml.make(sitemap, templates, destination) +function sitemapxml.make(sitedata, templates, destination) local _sitemapxml = etlua.compile(file.read(template.find(templates, 'sitemap.xml'))) - local sitemapxml_xml = _sitemapxml({sitemap = sitemap}) + local sitemapxml_xml = _sitemapxml({sitemap = sitedata}) local sitemapxml_xml_path = destination..'sitemap.xml' return sitemapxml_xml, sitemapxml_xml_path