satelito

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

commit 8da7cbfc27d0ebe08cae3e6c0f1516c1fa4beceb
parent c67b23f3365eaad48f45a5f812e6f904b1d8e4f8
Author: Hugo Soucy <hugo@soucy.cc>
Date:   Sun, 10 Oct 2021 09:29:46 -0400

Merge branch 'dev-3' into dev

Diffstat:
Msatelito-dev-2.rockspec | 1+
Msatelito/assets.lua | 3++-
Msatelito/dirtree.lua | 34+++++++++++++++++-----------------
Msatelito/feed.lua | 1-
Msatelito/init.lua | 145+++++++++++++++++++++++++++++++++++++------------------------------------------
Asatelito/lib/slugify.lua | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msatelito/site.lua | 42++++++++++++++++++++++++++++++++++++++++++
Asatelito/sitemapxml.lua | 16++++++++++++++++
8 files changed, 251 insertions(+), 97 deletions(-)

diff --git a/satelito-dev-2.rockspec b/satelito-dev-2.rockspec @@ -31,6 +31,7 @@ build = { ["satelito.model"] = "satelito/model.lua", ["satelito.page"] = "satelito/page.lua", ["satelito.site"] = "satelito/site.lua", + ["satelito.sitemapxml"] = "satelito/sitemapxml.lua", ["satelito.template"] = "satelito/template.lua", ["satelito.lib.lume.lume"] = "satelito/lib/lume/lume.lua", ["satelito.lib.lume.test.test"] = "satelito/lib/lume/test/test.lua", diff --git a/satelito/assets.lua b/satelito/assets.lua @@ -21,7 +21,8 @@ function assets.export(filemeta) if not lfs.attributes(target) or lfs.attributes(source).modification > lfs.attributes(target).modification then - print(source, target) + print('=> Moving assets ...') + print('====> '..source..' => '..target) os.execute('cp ' .. source .. ' ' .. target) end diff --git a/satelito/dirtree.lua b/satelito/dirtree.lua @@ -4,31 +4,31 @@ local dirtree = {} local lfs = require 'lfs' -- luafilesystem function dirtree.get(dir) - assert(dir and dir ~= '', 'directory parameter is missing or empty') + assert(dir and dir ~= '', 'directory parameter is missing or empty') - -- Removes slash if is one - if string.sub(dir, -1) == '/' then - dir = string.sub(dir, 1, -2) - end + -- Removes slash if is one + if string.sub(dir, -1) == '/' then + dir = string.sub(dir, 1, -2) + end - -- Main function of the coroutine (recursive) - local function yieldtree(dir) - for entry in lfs.dir(dir) do - if entry ~= '.' and entry ~= '..' then - entry = dir..'/'..entry + -- Main function of the coroutine (recursive) + local function yieldtree(dir) + for entry in lfs.dir(dir) do + if entry ~= '.' and entry ~= '..' then + entry = dir..'/'..entry - local attr = lfs.attributes(entry) + local attr = lfs.attributes(entry) - coroutine.yield(entry,dir,attr) + coroutine.yield(entry,dir,attr) - if attr.mode == 'directory' then - yieldtree(entry) - end - end + if attr.mode == 'directory' then + yieldtree(entry) end + end end + end - return coroutine.wrap(function() yieldtree(dir) end) + return coroutine.wrap(function() yieldtree(dir) end) end return dirtree diff --git a/satelito/feed.lua b/satelito/feed.lua @@ -14,5 +14,4 @@ function feed.make(filemeta, templates) return feed_xml, feed_xml_path end - return feed diff --git a/satelito/init.lua b/satelito/init.lua @@ -6,17 +6,15 @@ local inspect = require 'inspect' -- local lume = require 'satelito.lib.lume.lume' local model = require 'satelito.model' -local assets = require 'satelito.assets' local dirtree = require 'satelito.dirtree' -local feed = require 'satelito.feed' local file = require 'satelito.file' -local list = require 'satelito.list' +local sitemapxml = require 'satelito.sitemapxml' local site = require 'satelito.site' -local page = require 'satelito.page' -- local init local pipe local make +local exec local parser = argparse() :name 'satelito' @@ -32,37 +30,61 @@ pipe:flag('-e --export', 'Export the outputed HTML in the *config.paths.public_h -- Set 'make' command make = parser:command('make', 'Build the site from his directory.') make:flag('-e --export', 'Export the outputed HTML in the *config.paths.public_html* folder.') +-- Set the exec command +exec = parser:command('exec', 'Execute a script frome the bin directory') +exec:argument 'bin name' args = parser:parse() +if args['exec'] then + package.path = package.path .. ';'.. lfs.currentdir() ..'/?.lua' + + print(args['bin name'], inspect(lfs.currentdir())) + + if args['bin name'] then + dofile(lfs.currentdir() .. '/bin/'..args['bin name']) + end + +end + -- Example '$ satelito init' if args['init'] then os.execute('mkdir ~/satelito-sample') os.execute('git clone git://soucy.cc/satelito-sample.git ~/satelito-sample') - print('--------------------------------------------------------------------------------------------') + print('------------------------------------------------------------------------------------------------------') os.execute('rm -Rf ~/satelito-sample/.git') os.execute('ls -la ~/satelito-sample') - print('--------------------------------------------------------------------------------------------') + print('------------------------------------------------------------------------------------------------------') print('You should rename `~/satelito-sample` and edit `~/satelito-sample/config.lua` according to your needs.') - print('--------------------------------------------------------------------------------------------') + print('------------------------------------------------------------------------------------------------------') return end -- Example: '$ find site/content/ -name "*.md" | satelito pipe' if args['pipe'] then - local timestart = os.time() - local config - local templates + local configpath = false + local config = false + local contentdir = false local sitemap = {} + local templates + local timestart = os.time() + + print('=> Satelito is on ...') for filepath in (io.lines()) do if file.is_markdown(filepath) or file.is_html(filepath) then -- Get the site configuration's path - local configpath = site.get_config(filepath) + if not configpath then + print('=> Searching for the nearest config.lua ...') + configpath = site.get_config(filepath) + end -- Change the current directory to the config's directory - lfs.chdir(file.get_dirname(configpath)) + if lfs.currentdir()..'/' ~= file.get_dirname(configpath) then + print('=> Moving where the configuration file is located ...') + lfs.chdir(file.get_dirname(configpath)) + end -- If the site configuration file exists if file.exists(configpath) then @@ -70,77 +92,61 @@ if args['pipe'] then package.path = package.path .. ';'.. lfs.currentdir() ..'/?.lua' -- Set config.lua in a table - config = require 'config' + if not config then + print('=> Fetching the configuration file content ...') + config = require 'config' + end -- Absolute path to the 'content/' directory - local contentdir = lfs.currentdir() .. '/' .. config.paths.content + if not contentdir then + contentdir = lfs.currentdir() .. '/' .. config.paths.content + end -- Get the meta of the file local meta = model.set(filepath, config, contentdir) -- 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[meta.relpath] = lume.extend({}, meta) end end end + print('=> '..lume.count(sitemap)..' content found') + -- Array of templates + print('=> Fetching the templates ...') templates = lume.array(dirtree.get(lfs.currentdir() .. '/' .. config.paths.templates)) - -- Loop through the sitemap to create and export the HTML page - for k, v in pairs(sitemap) do - local html, html_path - local feed_xml, feed_xml_path - - v.root = sitemap - v.children = list.get_children(v.list, sitemap, v.asc) - - -- Make the page - html, html_path = page.make(v, templates) - - if args['export'] then - file.export(html_path, html) - else - print(html) - end - - if file.is_index(k) and args['export'] then - feed_xml, feed_xml_path = feed.make(v, templates) - - file.export(feed_xml_path, feed_xml) - end - - -- Copy assets to the public_html/ folder - if args['export'] then - assets.export(v) - end - end - - print('Satelito built ' .. lume.count(sitemap) .. ' HTML page(s) in approximately ' .. os.difftime(os.time(), timestart) .. ' second(s).') - return + print('=> Making the web site ...') + return site.make(sitemap, templates, args['export'], timestart) end -- 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' - local timestart = os.time() - local sitemap = {} - -- 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 - -- Array of templates - local templates = lume.array(dirtree.get(lfs.currentdir() .. '/' .. config.paths.templates)) + 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, config, contentdir) @@ -149,37 +155,20 @@ if args['make'] then end end - -- - for k, v in pairs(sitemap) do - local html, html_path - local feed_xml, feed_xml_path - - v.root = sitemap - v.children = list.get_children(v.list, sitemap, v.asc) - - -- Make the page - html, html_path = page.make(v, templates) - - -- Export the page - if args['export'] then - file.export(html_path, html) - else - print(html) - end - - if file.is_index(k) and args['export'] then - feed_xml, feed_xml_path = feed.make(v, templates) + print('=> '..lume.count(sitemap)..' content found') - file.export(feed_xml_path, feed_xml) - end + -- Array of templates + print('=> Fetching the templates ...') + templates = lume.array(dirtree.get(lfs.currentdir() .. '/' .. config.paths.templates)) - -- Copy assets to the public_html/ folder - if args['export'] then - assets.export(v) - end + if config.sitemapxml and args['export'] then + print('=> Making the sitemap.xml file ...') + local sitemapxml_xml, sitemapxml_xml_path = sitemapxml.make(sitemap, templates, config.paths.public_html) + file.export(sitemapxml_xml_path, sitemapxml_xml) end - print('Satelito built ' .. lume.count(sitemap) .. ' HTML page(s) in approximately ' .. os.difftime(os.time(), timestart) .. ' second(s).') + print('=> Making the web site ...') + return site.make(sitemap, templates, args['export'], timestart) else print('There is no "config.lua" here.') os.exit() diff --git a/satelito/lib/slugify.lua b/satelito/lib/slugify.lua @@ -0,0 +1,106 @@ +--[[ + Lua Slugify + + Creating URL slugs properly in Lua (including transliteration for + UTF-8) + + "Original version in JavaScript" + <https://ourcodeworld.com/articles/read/255/creating-url-slugs-properly-in-javascript-including-transliteration-for-utf-8> +]]-- + +local charmap = { + -- Latin + ["À"] = "A", ["Á"] = "A", ["Â"] = "A", ["Ã"] = "A", ["Ä"] = "A", ["Å"] = "A", ["Æ"] = "AE", ["Ç"] = "C", + ["È"] = "E", ["É"] = "E", ["Ê"] = "E", ["Ë"] = "E", ["Ì"] = "I", ["Í"] = "I", ["Î"] = "I", ["Ï"] = "I", + ["Ð"] = "D", ["Ñ"] = "N", ["Ò"] = "O", ["Ó"] = "O", ["Ô"] = "O", ["Õ"] = "O", ["Ö"] = "O", ["Ő"] = "O", + ["Ø"] = "O", ["Ù"] = "U", ["Ú"] = "U", ["Û"] = "U", ["Ü"] = "U", ["Ű"] = "U", ["Ý"] = "Y", ["Þ"] = "TH", + ["ß"] = "ss", + ["à"] = "a", ["á"] = "a", ["â"] = "a", ["ã"] = "a", ["ä"] = "a", ["å"] = "a", ["æ"] = "ae", ["ç"] = "c", + ["è"] = "e", ["é"] = "e", ["ê"] = "e", ["ë"] = "e", ["ì"] = "i", ["í"] = "i", ["î"] = "i", ["ï"] = "i", + ["ð"] = "d", ["ñ"] = "n", ["ò"] = "o", ["ó"] = "o", ["ô"] = "o", ["õ"] = "o", ["ö"] = "o", ["ő"] = "o", + ["ø"] = "o", ["ù"] = "u", ["ú"] = "u", ["û"] = "u", ["ü"] = "u", ["ű"] = "u", ["ý"] = "y", ["þ"] = "th", + ["ÿ"] = "y", + -- Latin symbols + ["©"] = "(c)", + -- Greek + ["Α"] = "A", ["Β"] = "B", ["Γ"] = "G", ["Δ"] = "D", ["Ε"] = "E", ["Ζ"] = "Z", ["Η"] = "H", ["Θ"] = "8", + ["Ι"] = "I", ["Κ"] = "K", ["Λ"] = "L", ["Μ"] = "M", ["Ν"] = "N", ["Ξ"] = "3", ["Ο"] = "O", ["Π"] = "P", + ["Ρ"] = "R", ["Σ"] = "S", ["Τ"] = "T", ["Υ"] = "Y", ["Φ"] = "F", ["Χ"] = "X", ["Ψ"] = "PS", ["Ω"] = "W", + ["Ά"] = "A", ["Έ"] = "E", ["Ί"] = "I", ["Ό"] = "O", ["Ύ"] = "Y", ["Ή"] = "H", ["Ώ"] = "W", ["Ϊ"] = "I", + ["Ϋ"] = "Y", + ["α"] = "a", ["β"] = "b", ["γ"] = "g", ["δ"] = "d", ["ε"] = "e", ["ζ"] = "z", ["η"] = "h", ["θ"] = "8", + ["ι"] = "i", ["κ"] = "k", ["λ"] = "l", ["μ"] = "m", ["ν"] = "n", ["ξ"] = "3", ["ο"] = "o", ["π"] = "p", + ["ρ"] = "r", ["σ"] = "s", ["τ"] = "t", ["υ"] = "y", ["φ"] = "f", ["χ"] = "x", ["ψ"] = "ps", ["ω"] = "w", + ["ά"] = "a", ["έ"] = "e", ["ί"] = "i", ["ό"] = "o", ["ύ"] = "y", ["ή"] = "h", ["ώ"] = "w", ["ς"] = "s", + ["ϊ"] = "i", ["ΰ"] = "y", ["ϋ"] = "y", ["ΐ"] = "i", + -- Turkish + ["Ş"] = "S", ["İ"] = "I", ["Ç"] = "C", ["Ü"] = "U", ["Ö"] = "O", ["Ğ"] = "G", + ["ş"] = "s", ["ı"] = "i", ["ç"] = "c", ["ü"] = "u", ["ö"] = "o", ["ğ"] = "g", + -- Russian + ["А"] = "A", ["Б"] = "B", ["В"] = "V", ["Г"] = "G", ["Д"] = "D", ["Е"] = "E", ["Ё"] = "Yo", ["Ж"] = "Zh", + ["З"] = "Z", ["И"] = "I", ["Й"] = "J", ["К"] = "K", ["Л"] = "L", ["М"] = "M", ["Н"] = "N", ["О"] = "O", + ["П"] = "P", ["Р"] = "R", ["С"] = "S", ["Т"] = "T", ["У"] = "U", ["Ф"] = "F", ["Х"] = "H", ["Ц"] = "C", + ["Ч"] = "Ch", ["Ш"] = "Sh", ["Щ"] = "Sh", ["Ъ"] = "", ["Ы"] = "Y", ["Ь"] = "", ["Э"] = "E", ["Ю"] = "Yu", + ["Я"] = "Ya", + ["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "yo", ["ж"] = "zh", + ["з"] = "z", ["и"] = "i", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", + ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "h", ["ц"] = "c", + ["ч"] = "ch", ["ш"] = "sh", ["щ"] = "sh", ["ъ"] = "", ["ы"] = "y", ["ь"] = "", ["э"] = "e", ["ю"] = "yu", + ["я"] = "ya", + -- Ukrainian + ["Є"] = "Ye", ["І"] = "I", ["Ї"] = "Yi", ["Ґ"] = "G", + ["є"] = "ye", ["і"] = "i", ["ї"] = "yi", ["ґ"] = "g", + -- Czech + ["Č"] = "C", ["Ď"] = "D", ["Ě"] = "E", ["Ň"] = "N", ["Ř"] = "R", ["Š"] = "S", ["Ť"] = "T", ["Ů"] = "U", + ["Ž"] = "Z", + ["č"] = "c", ["ď"] = "d", ["ě"] = "e", ["ň"] = "n", ["ř"] = "r", ["š"] = "s", ["ť"] = "t", ["ů"] = "u", + ["ž"] = "z", + -- Polish + ["Ą"] = "A", ["Ć"] = "C", ["Ę"] = "e", ["Ł"] = "L", ["Ń"] = "N", ["Ó"] = "o", ["Ś"] = "S", ["Ź"] = "Z", + ["Ż"] = "Z", + ["ą"] = "a", ["ć"] = "c", ["ę"] = "e", ["ł"] = "l", ["ń"] = "n", ["ó"] = "o", ["ś"] = "s", ["ź"] = "z", + ["ż"] = "z", + -- Latvian + ["Ā"] = "A", ["Č"] = "C", ["Ē"] = "E", ["Ģ"] = "G", ["Ī"] = "i", ["Ķ"] = "k", ["Ļ"] = "L", ["Ņ"] = "N", + ["Š"] = "S", ["Ū"] = "u", ["Ž"] = "Z", + ["ā"] = "a", ["č"] = "c", ["ē"] = "e", ["ģ"] = "g", ["ī"] = "i", ["ķ"] = "k", ["ļ"] = "l", ["ņ"] = "n", + ["š"] = "s", ["ū"] = "u", ["ž"] = "z", + -- -- Varia + -- ["@"] = "", +} + +local extend = function(list, ...) + local lists = {...} + + for i, source in ipairs(lists) do + for k, v in pairs(source) do + list[k] = source[k] + end + end + + return list +end + +local slugify = function(str, opt) + local defaults = { + separator = "-", + limit = nil, + customslug = nil + } + + local options = extend({}, defaults, opt) + local slug = str:lower() + + for k, v in pairs(charmap) do + slug = slug:gsub(tostring(k), charmap[k]) + end + + slug = (type(options.limit) == "number" and slug:sub(0, options.limit) or slug) + :gsub("%p", "") + :gsub("%s", options.separator) + :gsub(options.separator.."+", options.separator) + + return (type(options.customslug) == "string" and options.customslug or slug) +end + +return slugify diff --git a/satelito/site.lua b/satelito/site.lua @@ -2,6 +2,13 @@ local site = {} -- local lfs = require 'lfs' +-- +local assets = require 'satelito.assets' +local feed = require 'satelito.feed' +local file = require 'satelito.file' +local list = require 'satelito.list' +local lume = require 'satelito.lib.lume.lume' +local page = require 'satelito.page' --- From a filepath get the closest 'config.lua' by climbing the -- directory tree @@ -21,4 +28,39 @@ function site.get_config(filepath) return site.get_config(dir_parent) end +-- Make the site +function site.make(sitemap, templates, export, timestart) + for k, v in pairs(sitemap) do + local html, html_path + local feed_xml, feed_xml_path + + v.root = sitemap + v.children = list.get_children(v.list, sitemap, v.asc) + + -- Make the page + html, html_path = page.make(v, templates) + + -- Export the page + if export then + file.export(html_path, html) + else + print(html) + end + + -- Feed + if file.is_index(k) and export then + feed_xml, feed_xml_path = feed.make(v, templates) + file.export(feed_xml_path, feed_xml) + end + + -- Copy assets to the public_html/ folder + if export then + assets.export(v) + end + end + + print('--------------------------------------------------------------------------') + print('Satelito built ' .. lume.count(sitemap) .. ' HTML page(s) in approximately ' .. os.difftime(os.time(), timestart) .. ' second(s).') +end + return site diff --git a/satelito/sitemapxml.lua b/satelito/sitemapxml.lua @@ -0,0 +1,16 @@ +-- @module sitemapxml +local sitemapxml = {} +-- +local etlua = require 'etlua' +local file = require 'satelito.file' +local template = require 'satelito.template' + +function sitemapxml.make(sitemap, templates, destination) + local _sitemapxml = etlua.compile(file.read(template.find(templates, 'sitemap.xml'))) + local sitemapxml_xml = _sitemapxml({sitemap = sitemap}) + local sitemapxml_xml_path = destination..'sitemap.xml' + + return sitemapxml_xml, sitemapxml_xml_path +end + +return sitemapxml