ferron

Ferron is a fast and simple static website generator made with Lua.
Log | Files | Refs | Submodules | README | LICENSE

commit 0d1e88071242b5e378eabb4ed6f5b6546c46f3ee
parent 7a6ee6bbca6f5667ac4ae89e63cbfb6f1d2ca4bf
Author: Hugo Soucy <hugo@soucy.cc>
Date:   Mon, 17 Dec 2018 22:04:22 -0500

Merge branch 'master' of github.com:hs0ucy/Ferron

Diffstat:
Mferron.lua | 38++++++++++++++++++++++++++++++--------
Mferron/content.lua | 13+++++++++++++
Mferron/list.lua | 104++++++++++++++++++++++++++++++++++++-------------------------------------------
Mferron/page.lua | 99+++++++++++++++++++++++++++++++++++--------------------------------------------
Mferron/site.lua | 34++++++++++++++++++++++++++++++++--
Mferron/utilities/file-utils.lua | 73++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mferron/utilities/table-utils.lua | 56+++++++++++++++++++++++++++++++++++++++++++++-----------
7 files changed, 253 insertions(+), 164 deletions(-)

diff --git a/ferron.lua b/ferron.lua @@ -4,7 +4,7 @@ -- App's Globals -- Init the main namespace -Ferron = { site = {} } +Ferron = { files = {}, site = {} } -- Set if the build need to be in devmode Ferron.devmode = (arg[1] == "--dev" and true or false) @@ -12,6 +12,8 @@ if Ferron.devmode == true then inspect = require "inspect" end +inspect = require "inspect" + -- Ferron's Modules local site = require "ferron.site" local content = require "ferron.content" @@ -23,16 +25,16 @@ local plugin = require "ferron.plugin" local exec = require "ferron.utilities.exec" Ferron.init = { - site.setsite, - site.reset, + site.setsite, + site.reset, } Ferron.build = { - page.makepage, - list.makelist, - link.makeshorts, - link.makerewritemap, - static.move + page.init, + list.init, + link.makeshorts, + link.makerewritemap, + static.move } if arg[1] == "--set" and arg[2] == "content" then @@ -48,3 +50,23 @@ if arg[1] == "--run" and type(arg[2]) == "string" and arg[2] ~= "" then end return exec(Ferron.init, Ferron.build) + +--[[ + @TODO API + + - App + -- Execution + -- Installing + + - Site + -- Information + -- Listing + -- Selection + -- Creation + -- Reseting + -- Building + + - Content (markdown) + -- Creation + -- Listing +]]-- diff --git a/ferron/content.lua b/ferron/content.lua @@ -106,4 +106,17 @@ function content.makecontent(contenttype, contenttitle) end end +function content.getFiles() + local contentpath = assert(fileutils.isDirectory(Ferron.site.path .. Ferron.site.config.paths.content)) + + for file in fileutils.getdirtree(contentpath) do + if fileutils.isFile(file) then + table.insert(Ferron.files, file) + end + end + -- print(inspect(Ferron.files)) + -- os.exit("1") + return Ferron.files +end + return content diff --git a/ferron/list.lua b/ferron/list.lua @@ -57,67 +57,57 @@ local function setentries(directory, list_mt) return list_mt["entries"] end -function list.makelist() - local contentpath = Ferron.site.path .. Ferron.site.config.paths.content - local templatespath = Ferron.site.path .. Ferron.site.config.paths.templates +function list.render(listsource) + local list = listsource + local list_htmlpath = fileutils.sethtmlpath(fileutils.getrelpath(path.dirname(list))) + local list_conf = json.decode(fileutils.pullfilecontent(fileutils.removeextension(list) .. ".json")) + local list_conf_mt = setmetatable({}, { __index = list_conf }) + + print("§ Make a list page for the `" .. fileutils.getrelpath(path.dirname(list)) .. "` subdirectory ...") + + -- Convert the markdown file to HTML + list_conf_mt.content = markdown(fileutils.pullfilecontent(list)) + -- Set a dynamic permalink + list_conf_mt.permalink = Ferron.site.config.baseurl .. path.dirname(fileutils.getrelpath(fileutils.removeextension(list))) + -- Import site configuration in the metatable + list_conf_mt["site"] = Ferron.site.config + -- Set a table for the list of entries + list_conf_mt["entries"] = {} + + -- Set and get entries of the list + setentries(path.dirname(list), list_conf_mt) + + if not path.isdir(list_htmlpath) then + fileutils.mkdir(list_htmlpath) + end - path.each( - contentpath .. "/index.md", - function(list) - local list_htmlpath = fileutils.sethtmlpath(fileutils.getrelpath(path.dirname(list))) - local list_conf = json.decode(fileutils.pullfilecontent(fileutils.removeextension(list) .. ".json")) - local list_conf_mt = setmetatable({}, { __index = list_conf }) - - print("§ Make a list page for the `" .. fileutils.getrelpath(path.dirname(list)) .. "` subdirectory ...") - - -- Convert the markdown file to HTML - list_conf_mt.content = markdown(fileutils.pullfilecontent(list)) - -- Set a dynamic permalink - list_conf_mt.permalink = Ferron.site.config.baseurl .. path.dirname(fileutils.getrelpath(fileutils.removeextension(list))) - -- Import site configuration in the metatable - list_conf_mt["site"] = Ferron.site.config - -- Set a table for the list of entries - list_conf_mt["entries"] = {} - - -- Set and get entries of the list - setentries(path.dirname(list), list_conf_mt) - - if not path.isdir(list_htmlpath) then - fileutils.mkdir(list_htmlpath) - end + -- Create syndication feeds for each list + if list_conf.feed ~= false then + feed.makefeed(list_conf_mt, list_htmlpath) + end - -- Create syndication feeds for each list - if list_conf.feed ~= false then - feed.makefeed(list_conf_mt, list_htmlpath) - end + -- Move static files in the public HTML folder + static.dispatch(list) - -- Move static files in the public HTML folder - static.dispatch(list) - - print("==========") - - -- Update the JSON file data - fileutils.pushfilecontent( - fileutils.removeextension(list) .. ".json", - json.encode(list_conf, {indent = true, keyorder = Ferron.site.config.metaskeyorder}) - ) - - -- Build and push the HTML page - fileutils.pushfilecontent( - list_htmlpath .. "/" .. fileutils.getplainname(list) .. ".html", - templateutils.rendertemplate( - templateutils.selecttemplate(false, list_conf.template), - list_conf_mt, - templatespath .. "/partials" - ) - ) - end, - { - delay = false; -- use snapshot of directory - recurse = true; -- include subdirs - reverse = false; -- subdirs at first - } + print("==========") + + -- Build and push the HTML page + fileutils.pushfilecontent( + list_htmlpath .. "/" .. fileutils.getplainname(list) .. ".html", + templateutils.rendertemplate( + templateutils.selecttemplate(false, list_conf.template), + list_conf_mt, + Ferron.site.path .. Ferron.site.config.paths.templates .. "/partials" + ) ) end +function list.init() + local contentpath = assert(fileutils.isDirectory(Ferron.site.path .. Ferron.site.config.paths.content)) + + tableutils.each(list.render, tableutils.filter(fileutils.isIndex, tableutils.settable(fileutils.getdirtree(contentpath)))) + + return +end + return list diff --git a/ferron/page.lua b/ferron/page.lua @@ -7,74 +7,63 @@ local tableutils = require "ferron.utilities.table-utils" local templateutils = require "ferron.utilities.template-utils" local page = {} -function page.makepage() - local contentpath = Ferron.site.path .. Ferron.site.config.paths.content - local templatespath = Ferron.site.path .. Ferron.site.config.paths.templates - local shortlinks_db = path.isfile(Ferron.site.path .. Ferron.site.config.paths.data .. "/shortlinks.json") - local shortlinks_tb = (shortlinks_db ~= false and json.decode(fileutils.pullfilecontent(shortlinks_db)) or nil) +function page.isNotIndexPage(pagepath) + if fileutils.isMarkdown(pagepath) and not fileutils.isIndex(pagepath) then + return pagepath + end - -- Loop in the content directory - print("- Looking for markdown in " .. contentpath .. " ...") - print(" ¬ Then make the HTML pages of the site ...") + return +end - path.each( - contentpath .. "/*.md", - function(page) - if path.basename(page) ~= "index.md" then - local page_htmlpath = fileutils.sethtmlpath(fileutils.getrelpath(path.dirname(page))) - local page_conf = json.decode(fileutils.pullfilecontent(fileutils.removeextension(page) .. ".json")) - local page_conf_mt = setmetatable({}, { __index = page_conf }) - local page_key = page_conf.date .. "|" .. page_conf.datetime .. "|" .. fileutils.getrelpath(fileutils.removeextension(page)) +function page.render(pagesource) + local page = pagesource + local page_htmlpath = fileutils.sethtmlpath(fileutils.getrelpath(path.dirname(page))) + local page_conf = json.decode(fileutils.pullfilecontent(fileutils.removeextension(page) .. ".json")) + local page_conf_mt = setmetatable({}, { __index = page_conf }) + local page_key = page_conf.date .. "|" .. page_conf.datetime .. "|" .. fileutils.getrelpath(fileutils.removeextension(page)) - page_conf.id = "tag:" .. Ferron.site.config.domainname .. "," .. page_conf.date .. ":" .. string.sub(page_conf.date, 0, 4) .. "/" .. string.sub(page_conf.date, 6, 7) .. "/" .. fileutils.getplainname(page) + -- Set some dynamic configuration properties + page_conf_mt.content = markdown(fileutils.pullfilecontent(page)) + page_conf_mt.updated = os.date("%Y-%m-%dT%H:%M:%S", lfs.attributes(page).modification) + page_conf_mt.permalink = Ferron.site.config.baseurl .. (fileutils.getplainname(page) ~= "index" and fileutils.getrelpath(fileutils.removeextension(page)) .. ".html" or "") + page_conf_mt.rellink = (fileutils.getplainname(page) ~= "index" and fileutils.getrelpath(fileutils.removeextension(page)) .. ".html" or "") + page_conf_mt["site"] = Ferron.site.config - -- Convert the markdown file to HTML - -- And put it in a metatable - page_conf_mt.content = markdown(fileutils.pullfilecontent(page)) + if page_conf.id == nil then + page_conf_mt.id = "tag:" .. Ferron.site.config.domainname .. "," .. page_conf.date .. ":" .. string.sub(page_conf.date, 0, 4) .. "/" .. string.sub(page_conf.date, 6, 7) .. "/" .. fileutils.getplainname(page) + end - page_conf_mt.updated = os.date("%Y-%m-%dT%H:%M:%S", path.mtime(page)) - page_conf_mt.permalink = Ferron.site.config.baseurl .. (fileutils.getplainname(page) ~= "index" and fileutils.getrelpath(fileutils.removeextension(page)) .. ".html" or "") - page_conf_mt.rellink = (fileutils.getplainname(page) ~= "index" and fileutils.getrelpath(fileutils.removeextension(page)) .. ".html" or "") - page_conf_mt["site"] = Ferron.site.config + Ferron.site.pagestable[page_key] = page_conf_mt - -- If exists set the shortlink - if shortlinks_tb ~= nil and shortlinks_db ~= nil then - page_conf.shortlink = (shortlinks_tb[page_conf_mt.rellink] ~= nil and shortlinks_tb[page_conf_mt.rellink] or nil) - end + -- If the ancestors of the page dosen't exists make it + if not fileutils.isDirectory(page_htmlpath) then + fileutils.mkdir(page_htmlpath) + end - Ferron.site.pagestable[page_key] = page_conf_mt + -- Build and push the HTML page + fileutils.pushfilecontent( + page_htmlpath .. "/" .. fileutils.getplainname(page) .. ".html", + templateutils.rendertemplate( + templateutils.selecttemplate(false, page_conf.template), + page_conf_mt, + assert(fileutils.isDirectory(Ferron.site.path .. Ferron.site.config.paths.templates)) .. "/partials" + ) + ) - -- If the ancestors of the page dosen't exists make it - if not path.isdir(page_htmlpath) then - fileutils.mkdir(page_htmlpath) - end + --print(page) + return +end - -- Update the JSON file data - fileutils.pushfilecontent( - fileutils.removeextension(page) .. ".json", - json.encode(page_conf, {indent = true, keyorder = Ferron.site.config.metaskeyorder}) - ) +function page.init() + local contentpath = assert(fileutils.isDirectory(Ferron.site.path .. Ferron.site.config.paths.content)) - -- Build and push the HTML page - fileutils.pushfilecontent( - page_htmlpath .. "/" .. fileutils.getplainname(page) .. ".html", - templateutils.rendertemplate( - templateutils.selecttemplate(false, page_conf.template), - page_conf_mt, - templatespath .. "/partials" - ) - ) - end - end, - { - delay = false; -- use snapshot of directory - recurse = true; -- include subdirs - reverse = true; -- subdirs at first - } - ) + tableutils.each(page.render, tableutils.filter(page.isNotIndexPage, tableutils.settable(fileutils.getdirtree(contentpath)))) + print("==========") print("§ " .. tableutils.length(Ferron.site.pagestable) .. " HTML pages have been created.") print("==========") + + return --print(inspect(Ferron.site.pagestable)) end return page diff --git a/ferron/site.lua b/ferron/site.lua @@ -86,9 +86,39 @@ function site.makesite() end end +-- Reset the `public_html/` folder of the selected site function site.reset() - -- Reset the `public_html/` folder of the selected site - return fileutils.emptydirectory(Ferron.site.path .. Ferron.site.config.paths.html .. "/") + local publichtml = Ferron.site.path .. Ferron.site.config.paths.html + local function removefiles(dir) + local ok, errormsg + -- remove files from directory + for file in lfs.dir(dir) do + if file == "." or file == ".." or file == ".gitignore" then -- skip system files + -- do nothing + else + local thefile = dir.."/"..file + + if lfs.attributes(thefile, "mode") == "directory" then + removefiles(thefile) + else + ok, errormsg = os.remove(thefile) + + if not ok then + print("Error removing file: "..file..":"..errormsg) + end + end + end + end + + -- remove directory + ok, errormsg = os.remove(dir) + + if not ok then + print("Can't removing directory: "..dir..":"..errormsg) + end + end + + removefiles(publichtml) end return site diff --git a/ferron/utilities/file-utils.lua b/ferron/utilities/file-utils.lua @@ -6,22 +6,54 @@ local tableutils = require "ferron.utilities.table-utils" local fileutils = {} -function fileutils.isNonTextual(file) - local mimestable = Ferron.site.config.mimetypes +function fileutils.getbasename(filepath) + return string.gsub(filepath, "(.*/)(.*)", "%2") +end - return (tableutils.hasvalue(mimestable, mimetypes.guess(file)) and true or false) +function fileutils.isFile(filepath) + if lfs.attributes(filepath) == nil then + return false + elseif lfs.attributes(filepath).mode == "file" then + return filepath + end + + return false end --- loadlocalconfig -function fileutils.loadlocalconfig(file) - local env = {} - local chunk, err = loadfile(file, 'bt', env) +function fileutils.isDirectory(filepath) + if lfs.attributes(filepath) == nil then + return false + elseif lfs.attributes(filepath).mode == "directory" then + return filepath + end + + return false +end - if not err then - chunk() +function fileutils.isMarkdown(filepath) + if fileutils.isFile(filepath) + and (mimetypes.guess(filepath) == "text/x-markdown" + or path.extension(filepath) == ".md") then + return filepath end - return env, err + return +end + +function fileutils.isIndex(filepath) + if fileutils.isMarkdown(filepath) == false then + return false + elseif fileutils.getbasename(filepath) == "index.md" then + return filepath + end + + return false +end + +function fileutils.isNonTextual(file) + local mimestable = Ferron.site.config.mimetypes + + return (tableutils.hasvalue(mimestable, mimetypes.guess(file)) and true or false) end -- getdirtree @@ -111,26 +143,5 @@ function fileutils.shorturlencode(num) return str end -function fileutils.emptydirectory(dirpath) - path.each( - dirpath .. "*", - function(P, mode) - if mode == 'directory' then - path.rmdir(P) - elseif P ~= dirpath .. ".gitignore" then - path.remove(P) - end - - return - end, - { - param = "fm"; -- request full path and mode - delay = true; -- use snapshot of directory - recurse = true; -- include subdirs - reverse = true; -- subdirs at first - } - ) -end - -- Export `fileutils` module return fileutils diff --git a/ferron/utilities/table-utils.lua b/ferron/utilities/table-utils.lua @@ -1,17 +1,6 @@ -- local tableutils = {} --- protecttable -function tableutils.protecttable(tbl) - return setmetatable({}, { - __index = tbl, - __newindex = function(t, key, value) - error("attempting to change constant " .. - tostring(key) .. " to " .. tostring(value), 2) - end - }) -end - -- sortdescendingpairs function tableutils.sortdescendingpairs(t) local keys = {} @@ -81,4 +70,49 @@ function tableutils.extend(list, ...) return list end + +function tableutils.settable(...) + local t = {} + + for x in ... do t[#t + 1] = x end + + return t +end + +-- each(function, table) +-- ex.: tableutils.each(double, {1,2,3}) -> {2,4,6} +function tableutils.each(func, tbl) + for i,v in pairs(tbl) do + func(v) + end + + return +end + +-- map(function, table) +-- ex.: tableutils.map(double, {1,2,3}) -> {2,4,6} +function tableutils.map(func, tbl) + local newtbl = {} + + for i,v in pairs(tbl) do + newtbl[i] = func(v) + end + + return newtbl +end + +-- filter(function, table) + -- ex.: tableutils.filter(is_even, {1,2,3,4}) -> {2,4} + function tableutils.filter(func, tbl) + local newtbl= {} + + for i,v in pairs(tbl) do + if func(v) then + newtbl[i] = v + end + end + + return newtbl + end + return tableutils