git.m455.casa

wg

clone url: git://git.m455.casa/wg


dists/wg.lua

1 #!/usr/bin/env lua
2 -- wg - A static website generator in Fennel
3 -- Author: Jesse Laprade (m455)
4 -- License: AGPL3 (https://www.gnu.org/licenses/agpl-3.0.en.html)
5 -- Source: https://git.m455.casa/m455/wg
6 local ffs
7 package.preload["ffs"] = package.preload["ffs"] or function(...)
8 local ffs = {}
9 ffs["shell->sequence"] = function(command)
10 local file_handle = io.popen(command, "r")
11 local function close_handlers_0_(ok_0_, ...)
12 file_handle:close()
13 if ok_0_ then
14 return ...
15 else
16 return error(..., 0)
17 end
18 end
19 local function _0_()
20 local tbl_0_ = {}
21 for i in file_handle:lines() do
22 tbl_0_[(#tbl_0_ + 1)] = i
23 end
24 return tbl_0_
25 end
26 return close_handlers_0_(xpcall(_0_, (package.loaded.fennel or debug).traceback))
27 end
28 ffs["directory-exists?"] = function(directory)
29 local command = string.format("test -d %s && echo exists", directory)
30 if (ffs["shell->sequence"](command))[1] then
31 return true
32 else
33 return false
34 end
35 end
36 ffs["directory-create"] = function(dir)
37 return os.execute(string.format("mkdir -p %s", dir))
38 end
39 ffs["directory-contents"] = function(dir)
40 return ffs["shell->sequence"](string.format("ls %s", dir))
41 end
42 ffs["file-exists?"] = function(file)
43 local command = string.format("test -f %s && echo exists", file)
44 if (ffs["shell->sequence"](command))[1] then
45 return true
46 else
47 return false
48 end
49 end
50 ffs["file-create"] = function(file)
51 return io.close(io.open(file, "w"))
52 end
53 ffs["file-write"] = function(file, data, mode)
54 local file_out = io.open(file, mode)
55 local function close_handlers_0_(ok_0_, ...)
56 file_out:close()
57 if ok_0_ then
58 return ...
59 else
60 return error(..., 0)
61 end
62 end
63 local function _0_()
64 return file_out:write(data)
65 end
66 return close_handlers_0_(xpcall(_0_, (package.loaded.fennel or debug).traceback))
67 end
68 ffs["file->lines"] = function(file)
69 local tbl_0_ = {}
70 for line in io.lines(file) do
71 tbl_0_[(#tbl_0_ + 1)] = line
72 end
73 return tbl_0_
74 end
75 ffs["path-exists?"] = function(path)
76 return (ffs["file-exists?"](path) or ffs["directory-exists?"](path))
77 end
78 ffs["path-copy"] = function(source, dest)
79 return os.execute(string.format("cp -r %s %s", source, dest))
80 end
81 ffs["path-delete"] = function(path)
82 return os.execute(string.format("rm -rf %s", path))
83 end
84 ffs["paths-missing"] = function(mode, required_paths)
85 local func
86 do
87 local _0_0 = mode
88 if (_0_0 == "files") then
89 func = ffs["file-exists?"]
90 elseif (_0_0 == "directories") then
91 func = ffs["directory-exists?"]
92 else
93 func = nil
94 end
95 end
96 local tbl_0_ = {}
97 for _, path in ipairs(required_paths) do
98 local _1_
99 if not func(path) then
100 _1_ = path
101 else
102 _1_ = nil
103 end
104 tbl_0_[(#tbl_0_ + 1)] = _1_
105 end
106 return tbl_0_
107 end
108 return ffs
109 end
110 ffs = require("ffs")
111 local directories_required = {"build", "layout", "copy", "convert"}
112 local files_required = {footer = "layout/footer.md", header = "layout/header.md", index = "convert/index.md", style = "copy/style.css"}
113 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.")
114 local contents_style = ("body {\n" .. " background-color: pink;\n" .. "}\n\n" .. "code {\n" .. " background-color: black;\n" .. " color: pink;\n" .. "}")
115 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")
116 local contents_footer = ("</main>\n" .. "<hr/>\n" .. "<footer>This website was built with [wg](https://git.m455.casa/m455/wg)</footer>\n")
117 local function print_format(str, ...)
118 return print(string.format(str, ...))
119 end
120 local function print_missing_files()
121 print("Error: wg has not been initialized or there are missing files.")
122 print("If wg has been initialized, try running the following command:")
123 print(" wg repair")
124 print("If wg has not been initialized, try running the following command:")
125 print(" wg init")
126 print("For more help, type the following command:")
127 return print(" wg help")
128 end
129 local function required_paths_missing_3f()
130 return ((#ffs["paths-missing"]("directories", directories_required) > 0) or (#ffs["paths-missing"]("files", files_required) > 0))
131 end
132 local function build_directory_has_contents_3f()
133 return (#ffs["directory-contents"]("build") > 0)
134 end
135 local function init_2fstart()
136 for _, dir in ipairs(directories_required) do
137 print_format("Creating '%s/'...", dir)
138 ffs["directory-create"](dir)
139 end
140 for _, file in ipairs({files_required.index, files_required.style, files_required.header, files_required.footer}) do
141 print_format("Creating '%s'...", file)
142 ffs["file-create"](file)
143 end
144 ffs["file-write"](files_required.index, contents_index, "w")
145 ffs["file-write"](files_required.style, contents_style, "w")
146 ffs["file-write"](files_required.header, contents_header, "w")
147 ffs["file-write"](files_required.footer, contents_footer, "w")
148 return print("Initialization complete!")
149 end
150 local function init_2fread_input()
151 local input = io.read(1)
152 if (input == "y") then
153 return init_2fstart()
154 else
155 return print("Cancelled the creation of the required directories and files.")
156 end
157 end
158 local function init_2fprompt()
159 print("The following directories and files will be created:")
160 for _, dir in ipairs(directories_required) do
161 print_format(" %s/", dir)
162 end
163 for _, file in pairs(files_required) do
164 print_format(" %s", file)
165 end
166 print("This will overwrite any files with the same names as the files above.")
167 print("Are you sure you want to do this? (y/n)")
168 io.write("> ")
169 return init_2fread_input()
170 end
171 local function init()
172 if required_paths_missing_3f() then
173 return init_2fprompt()
174 else
175 return print("The required directories and files already exist.")
176 end
177 end
178 local function serve()
179 if required_paths_missing_3f() then
180 return print_missing_files()
181 else
182 if build_directory_has_contents_3f() then
183 return os.execute("python3 -m http.server 8000 --directory build/")
184 else
185 print("Error: 'build/' directory has no contents.")
186 print("Try running the following command first:")
187 return print(" wg build")
188 end
189 end
190 end
191 local function clean_2fstart()
192 print("Deleting contents of 'build/' directory...")
193 ffs["path-delete"](("build" .. "/*"))
194 return print("Cleaning complete!")
195 end
196 local function clean_2fprompt()
197 print("Cleaning will delete everything in the 'build/' directory.")
198 print("Do you want to continue? (y/n)")
199 io.write("> ")
200 local input = io.read(1)
201 if (input == "y") then
202 return clean_2fstart()
203 else
204 return print("Cancelled the deletion of the 'build/' directory's contents.")
205 end
206 end
207 local function clean()
208 if required_paths_missing_3f() then
209 return print_missing_files()
210 else
211 if build_directory_has_contents_3f() then
212 return clean_2fprompt()
213 else
214 return print("'build/' directory empty. Nothing to clean.")
215 end
216 end
217 end
218 local function markdown__3ehtml(source_file, destination_file)
219 return os.execute(string.format("pandoc -s -c /style.css %s %s %s -o %s --shift-heading-level-by=1", files_required.header, source_file, files_required.footer, destination_file))
220 end
221 local function build_2fconvert(dir)
222 for _, path in ipairs(ffs["directory-contents"](dir)) do
223 local source_path = (dir .. "/" .. path)
224 if (ffs["file-exists?"](source_path) and string.match(source_path, ".md")) then
225 local destination_dir = string.gsub(string.gsub(source_path, "(.*/).*.md", "%1"), "^convert/", "build/")
226 local destination_file = string.gsub(string.gsub(source_path, ".md", ".html"), "^convert/", "build/")
227 if not ffs["directory-exists?"](destination_dir) then
228 ffs["directory-create"](destination_dir)
229 end
230 markdown__3ehtml(source_path, destination_file)
231 else
232 if ffs["directory-exists?"](source_path) then
233 build_2fconvert(source_path)
234 end
235 end
236 end
237 return nil
238 end
239 local function build()
240 if required_paths_missing_3f() then
241 return print_missing_files()
242 else
243 if (#ffs["directory-contents"]("copy") == 0) then
244 print("No directories or files found in the 'copy/' directory. Skipping...")
245 else
246 print("Copying files in 'copy/' directory...")
247 ffs["path-copy"](("copy" .. "/*"), "build")
248 print("Copying complete!")
249 end
250 if (#ffs["directory-contents"]("convert") == 0) then
251 return print("No directories or files found in the 'convert/' directory. Skipping...")
252 else
253 print("Converting files in 'convert/' directory...")
254 build_2fconvert("convert")
255 return print("Conversion complete!")
256 end
257 end
258 end
259 local function repair_2fstart()
260 for _, dir in ipairs(directories_required) do
261 if not ffs["directory-exists?"](dir) then
262 print_format("Creating '%s/'...", dir)
263 ffs["directory-create"](dir)
264 end
265 end
266 for _, file in ipairs({files_required.index, files_required.style, files_required.header, files_required.footer}) do
267 if not ffs["file-exists?"](file) then
268 print_format("Creating '%s'...", file)
269 ffs["file-create"](file)
270 local make_file
271 local function _0_(file0, contents)
272 return ffs["file-write"](file0, contents, "w")
273 end
274 make_file = _0_
275 local _1_0 = file
276 if (_1_0 == files_required.index) then
277 make_file(files_required.index, contents_index)
278 elseif (_1_0 == files_required.style) then
279 make_file(files_required.style, contents_style)
280 elseif (_1_0 == files_required.header) then
281 make_file(files_required.header, contents_header)
282 elseif (_1_0 == files_required.footer) then
283 make_file(files_required.footer, contents_footer)
284 end
285 end
286 end
287 return print("Repair complete!")
288 end
289 local function repair_2fread_input()
290 local input = io.read(1)
291 if (input == "y") then
292 return repair_2fstart()
293 else
294 return print("Cancelled the repairs.")
295 end
296 end
297 local function repair_2fprompt()
298 print("The following files or directories are missing:")
299 for _, dir in ipairs(directories_required) do
300 if not ffs["directory-exists?"](dir) then
301 print_format(" %s/", dir)
302 end
303 end
304 for _, file in pairs(files_required) do
305 if not ffs["file-exists?"](file) then
306 print_format(" %s", file)
307 end
308 end
309 print("Do you want to create these files? (y/n)")
310 io.write("> ")
311 return repair_2fread_input()
312 end
313 local function repair()
314 if required_paths_missing_3f() then
315 return repair_2fprompt()
316 else
317 return print("The required directories and files already exist.")
318 end
319 end
320 local function help()
321 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" .. " repair\n" .. " Looks for and creates missing files or directories.\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"))
322 end
323 local function main(arg_tbl)
324 local _0_0 = arg_tbl
325 if ((type(_0_0) == "table") and ((_0_0)[1] == "init") and ((_0_0)[2] == nil)) then
326 return init()
327 elseif ((type(_0_0) == "table") and ((_0_0)[1] == "build") and ((_0_0)[2] == nil)) then
328 return build()
329 elseif ((type(_0_0) == "table") and ((_0_0)[1] == "serve") and ((_0_0)[2] == nil)) then
330 return serve()
331 elseif ((type(_0_0) == "table") and ((_0_0)[1] == "clean") and ((_0_0)[2] == nil)) then
332 return clean()
333 elseif ((type(_0_0) == "table") and ((_0_0)[1] == "repair") and ((_0_0)[2] == nil)) then
334 return repair()
335 elseif ((type(_0_0) == "table") and ((_0_0)[1] == "help") and ((_0_0)[2] == nil)) then
336 return help()
337 else
338 local _ = _0_0
339 print("For more information, type the following command:")
340 return print(" wg help")
341 end
342 end
343 return main(arg)