You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

295 lines
12 KiB

#!/usr/bin/env lua
-- wg - A static website generator in Fennel
-- Author: Jesse Laprade (m455)
-- License: AGPL3 (https://www.gnu.org/licenses/agpl-3.0.en.html)
-- Source: https://git.m455.casa/m455/wg
local ffs = nil
package.preload["ffs"] = package.preload["ffs"] or function(...)
local ffs = {}
ffs["shell->sequence"] = function(command)
local seq = {}
do
local file_handle = io.popen(command, "r")
local function close_handlers_0_(ok_0_, ...)
file_handle:close()
if ok_0_ then
return ...
else
return error(..., 0)
end
end
local function _0_()
for i in file_handle:lines() do
table.insert(seq, i)
end
return nil
end
close_handlers_0_(xpcall(_0_, (package.loaded.fennel or debug).traceback))
end
return seq
end
ffs["path-exists?"] = function(path)
local file_handle = io.open(path, "r")
if file_handle then
return file_handle:close()
else
return false
end
end
ffs["path-copy"] = function(source, dest)
return os.execute(string.format("cp -r %s %s", source, dest))
end
ffs["path-delete"] = function(path)
return os.execute(string.format("rm -rf %s", path))
end
ffs["directory-exists?"] = function(directory)
local str, items_found = string.gsub(directory, "/$", "")
local dir_to_check = nil
if (0 > items_found) then
dir_to_check = directory
else
dir_to_check = (str .. "/")
end
return ffs["path-exists?"](dir_to_check)
end
ffs["directory-create"] = function(dir)
return os.execute(string.format("mkdir -p %s", dir))
end
ffs["directory-contents"] = function(dir)
return ffs["shell->sequence"](string.format("ls %s", dir))
end
ffs["file-exists?"] = function(file)
return (ffs["path-exists?"](file) and not ffs["directory-exists?"](file))
end
ffs["file-create"] = function(file)
return io.close(io.open(file, "w"))
end
ffs["file-write"] = function(file, data, mode)
local file_out = io.open(file, mode)
local function close_handlers_0_(ok_0_, ...)
file_out:close()
if ok_0_ then
return ...
else
return error(..., 0)
end
end
local function _0_()
return file_out:write(data)
end
return close_handlers_0_(xpcall(_0_, (package.loaded.fennel or debug).traceback))
end
ffs["file->lines"] = function(file)
local seq = {}
for line in io.lines(file) do
table.insert(seq, line)
end
return seq
end
ffs["paths-missing"] = function(mode, required_paths)
local seq = {}
local func = nil
do
local _0_0 = mode
if (_0_0 == "files") then
func = ffs["file-exists?"]
elseif (_0_0 == "directories") then
func = ffs["directory-exists?"]
elseif (_0_0 == "paths") then
func = ffs["path-exists?"]
else
func = nil
end
end
for _, path in pairs(required_paths) do
if not func(path) then
table.insert(seq, path)
end
end
return seq
end
return ffs
end
ffs = require("ffs")
local directories_required = {"build", "layout", "copy", "convert"}
local files_required = {footer = "layout/footer.md", header = "layout/header.md", index = "convert/index.md", style = "copy/style.css"}
local contents_index = ("This is some default text found in `convert/index.md`.\n\n" .. "If you want to change the look of your website, you can\n" .. "find the CSS styles in `copy/style.css`.\n\n" .. "To change the header and footer, check out the `layout/` directory.")
local contents_style = ("body {\n" .. " background-color: pink;\n" .. "}\n\n" .. "code {\n" .. " background-color: black;\n" .. " color: pink;\n" .. "}")
local contents_header = ("---\n" .. "title: 'The title of your website'\n" .. "lang: en\n" .. "header-includes:\n" .. " <meta name=\"author\" content=\"Your name\"/>\n" .. " <meta name=\"description\" content=\"Some description\"/>\n" .. " <meta name=\"keywords\" content=\"programming, documentation\"/>\n" .. "---" .. "\n" .. "<nav>[Home](/) - [Another home link](/index.html)</nav>\n" .. "<hr/>\n" .. "<main>\n")
local contents_footer = ("</main>\n" .. "<hr/>\n" .. "<footer>This website was built with [wg](https://git.m455.casa/m455/wg)</footer>\n")
local function print_format(str, ...)
return print(string.format(str, ...))
end
local function print_not_initialized()
print("Error: wg has not been initialized or there are missing files.")
print("Try running the following command to initialize or check for missing files:")
print(" wg init")
print("For more information, type the following command:")
return print(" wg help")
end
local function required_paths_missing_3f()
return ((#ffs["paths-missing"]("directories", directories_required) > 0) or (#ffs["paths-missing"]("files", files_required) > 0))
end
local function build_directory_has_contents_3f()
return (#ffs["directory-contents"]("build") > 0)
end
local function init_2fstart()
for _, dir in ipairs(directories_required) do
if not ffs["directory-exists?"](dir) then
print_format("Creating '%s/'...", dir)
ffs["directory-create"](dir)
end
end
local file_index = files_required.index
local file_style = files_required.style
local file_header = files_required.header
local file_footer = files_required.footer
local files_required_seq = {file_index, file_style, file_header, file_footer}
for _, file in ipairs(files_required_seq) do
if not ffs["file-exists?"](file) then
print_format("Creating '%s'...", file)
ffs["file-create"](file)
end
end
ffs["file-write"](file_index, contents_index, "w")
ffs["file-write"](file_style, contents_style, "w")
ffs["file-write"](file_header, contents_header, "w")
ffs["file-write"](file_footer, contents_footer, "w")
return print("Initialization complete!")
end
local function init_2fread_input()
local input = io.read(1)
if (input == "y") then
return init_2fstart()
else
return print("Cancelled the creation of the required directories and files")
end
end
local function init_2fprompt()
print("The following directories and files need to be created:")
for _, dir in ipairs(directories_required) do
if not ffs["directory-exists?"](dir) then
print_format(" %s/", dir)
end
end
for _, file in pairs(files_required) do
if not ffs["file-exists?"](file) then
print_format(" %s", file)
end
end
print("Do you want to continue? (y/n)")
io.write("> ")
return init_2fread_input()
end
local function init()
if required_paths_missing_3f() then
return init_2fprompt()
else
return print("The required directories and files exist.")
end
end
local function serve()
if required_paths_missing_3f() then
return print_not_initialized()
else
if build_directory_has_contents_3f() then
return os.execute("python3 -m http.server 8000 --directory build/")
else
print("Error: 'build/' directory has no contents.")
print("Try running the following command first:")
return print(" wg build")
end
end
end
local function clean_2fstart()
print("Deleting contents of 'build/' directory...")
ffs["path-delete"](("build" .. "/*"))
return print("Cleaning complete!")
end
local function clean_2fprompt()
print("Cleaning will delete everything in the 'build/' directory.")
print("Do you want to continue? (y/n)")
io.write("> ")
local input = io.read(1)
if (input == "y") then
return clean_2fstart()
else
return print("Cancelled the deletion of the 'build/' directory's contents.")
end
end
local function clean()
if required_paths_missing_3f() then
return print_not_initialized()
else
if build_directory_has_contents_3f() then
return clean_2fprompt()
else
return print("'build/' directory empty. Nothing to clean.")
end
end
end
local function markdown__3ehtml(source, destination)
return os.execute(string.format("pandoc -s -c /style.css %s %s %s -o %s --base-header-level=2", files_required.header, source, files_required.footer, destination))
end
local function build_2fconvert(dir)
for _, v in ipairs(ffs["directory-contents"](dir)) do
local source = (dir .. "/" .. v)
if (ffs["file-exists?"](source) and string.match(source, ".md")) then
local destination_dir_pre = string.gsub(source, "(.*/).*.md", "%1")
local destination_dir = string.gsub(destination_dir_pre, "^convert/", "build/")
local destination_pre = string.gsub(source, ".md", ".html")
local destination = string.gsub(destination_pre, "^convert/", "build/")
if not ffs["directory-exists?"](destination_dir) then
ffs["directory-create"](destination_dir)
end
markdown__3ehtml(source, destination)
else
if ffs["directory-exists?"](source) then
build_2fconvert(source)
end
end
end
return nil
end
local function build()
if required_paths_missing_3f() then
return print_not_initialized()
else
if (#ffs["directory-contents"]("copy") == 0) then
print("No directories or files found in the 'copy/' directory. Skipping...")
else
print("Copying files in 'copy/' directory...")
ffs["path-copy"](("copy" .. "/*"), "build")
print("Copying complete!")
end
if (#ffs["directory-contents"]("convert") == 0) then
return print("No directories or files found in the 'convert/' directory. Skipping...")
else
print("Converting files in 'convert/' directory...")
build_2fconvert("convert")
return print("Conversion complete!")
end
end
end
local function help()
return print(("wg\n" .. " A static website generator written in Fennel.\n" .. "\n" .. "Author\n" .. " Jesse Laprade (m455)\n" .. "\n" .. "License\n" .. " AGPL3 (https://www.gnu.org/licenses/agpl-3.0.en.html)\n" .. "\n" .. "Source\n" .. " https://git.m455.casa/m455/wg\n" .. "\n" .. "Commands\n" .. " init\n" .. " Creates required directories and files in the current directory.\n" .. "\n" .. " build\n" .. " Recursively copies directories and files from the 'copy/'\n" .. " directory into the 'build/' directory, preserving the directory\n" .. " structure of the 'copy/' directory.\n" .. "\n" .. " Recursively converts Markdown files in the 'convert/' directory\n" .. " to HTML files in the 'build/' directory, preserving the\n" .. " directory structure of the 'convert/' directory.\n" .. "\n" .. " serve\n" .. " Serves files in the 'build/' directory on port 8000, allowing\n" .. " you to see how your website will look locally before it goes\n" .. " live.\n" .. "\n" .. " clean\n" .. " Deletes all contents of the 'build/' directory.\n" .. "\n" .. " help\n" .. " Displays this help message.\n" .. "\n" .. "Example usage\n" .. " wg init\n" .. " wg build\n" .. " wg serve\n" .. " wg clean\n" .. " wg help\n"))
end
local function main(arg_tbl)
local _0_0 = arg_tbl
if ((type(_0_0) == "table") and (_0_0[1] == "init") and (_0_0[2] == nil)) then
return init()
elseif ((type(_0_0) == "table") and (_0_0[1] == "build") and (_0_0[2] == nil)) then
return build()
elseif ((type(_0_0) == "table") and (_0_0[1] == "serve") and (_0_0[2] == nil)) then
return serve()
elseif ((type(_0_0) == "table") and (_0_0[1] == "clean") and (_0_0[2] == nil)) then
return clean()
elseif ((type(_0_0) == "table") and (_0_0[1] == "help") and (_0_0[2] == nil)) then
return help()
else
local _ = _0_0
print("For more information, type the following command:")
return print(" wg help")
end
end
return main(arg)