clone url: git://git.m455.casa/wg
src/wg.fnl
1 | (local ffs (require :ffs)) |
2 |
|
3 | (local directories-required [:build :layout :copy :convert]) |
4 |
|
5 | (local files-required {:index "convert/index.md" |
6 | :style "copy/style.css" |
7 | :header "layout/header.md" |
8 | :footer "layout/footer.md"}) |
9 |
|
10 | (local contents-index |
11 | (.. "This is some default text found in `convert/index.md`.\n\n" |
12 | "If you want to change the look of your website, you can\n" |
13 | "find the CSS styles in `copy/style.css`.\n\n" |
14 | "To change the header and footer, check out the `layout/` directory.")) |
15 |
|
16 | (local contents-style |
17 | (.. "body {\n" |
18 | " background-color: pink;\n" |
19 | "}\n\n" |
20 | "code {\n" |
21 | " background-color: black;\n" |
22 | " color: pink;\n" |
23 | "}")) |
24 |
|
25 | (local contents-header |
26 | (.. "---\n" |
27 | "title: 'The title of your website'\n" |
28 | "lang: en\n" |
29 | "header-includes:\n" |
30 | " <meta name=\"author\" content=\"Your name\"/>\n" |
31 | " <meta name=\"description\" content=\"Some description\"/>\n" |
32 | " <meta name=\"keywords\" content=\"programming, documentation\"/>\n" |
33 | "---" |
34 | "\n" |
35 | "<nav>[Home](/) - [Another home link](/index.html)</nav>\n" |
36 | "<hr/>\n" |
37 | "<main>\n")) |
38 |
|
39 | (local contents-footer |
40 | (.. "</main>\n" |
41 | "<hr/>\n" |
42 | "<footer>This website was built with [wg](https://git.m455.casa/m455/wg)</footer>\n")) |
43 |
|
44 | (fn print-format [str ...] |
45 | (print (string.format str ...))) |
46 |
|
47 | (fn print-missing-files [] |
48 | (print "Error: wg has not been initialized or there are missing files.") |
49 | (print "If wg has been initialized, try running the following command:") |
50 | (print " wg repair") |
51 | (print "If wg has not been initialized, try running the following command:") |
52 | (print " wg init") |
53 | (print "For more help, type the following command:") |
54 | (print " wg help")) |
55 |
|
56 | ;; --------------------------------------- |
57 | ;; Path utils |
58 | ;; --------------------------------------- |
59 | (fn required-paths-missing? [] |
60 | (or (> (length (ffs.paths-missing :directories directories-required)) 0) |
61 | (> (length (ffs.paths-missing :files files-required)) 0))) |
62 |
|
63 | (fn build-directory-has-contents? [] |
64 | (> (length (ffs.directory-contents :build)) 0)) |
65 |
|
66 | ;; --------------------------------------- |
67 | ;; init |
68 | ;; --------------------------------------- |
69 | (fn init/start [] |
70 | ;; Create required directories |
71 | (each [_ dir (ipairs directories-required)] |
72 | (print-format "Creating '%s/'..." dir) |
73 | (ffs.directory-create dir)) |
74 | ;; Create and populate required files |
75 | ;; Create required files |
76 | (each [_ file (ipairs [files-required.index |
77 | files-required.style |
78 | files-required.header |
79 | files-required.footer])] |
80 | (print-format "Creating '%s'..." file) |
81 | (ffs.file-create file)) |
82 | ;; Populate required files |
83 | (ffs.file-write files-required.index contents-index :w) |
84 | (ffs.file-write files-required.style contents-style :w) |
85 | (ffs.file-write files-required.header contents-header :w) |
86 | (ffs.file-write files-required.footer contents-footer :w) |
87 | (print "Initialization complete!")) |
88 |
|
89 | (fn init/read-input [] |
90 | (let [input (io.read 1)] |
91 | (if (= input :y) |
92 | (init/start) |
93 | (print "Cancelled the creation of the required directories and files.")))) |
94 |
|
95 | (fn init/prompt [] |
96 | (print "The following directories and files will be created:") |
97 | (each [_ dir (ipairs directories-required)] |
98 | (print-format " %s/" dir)) |
99 | (each [_ file (pairs files-required)] |
100 | (print-format " %s" file)) |
101 | (print "This will overwrite any files with the same names as the files above.") |
102 | (print "Are you sure you want to do this? (y/n)") |
103 | (io.write "> ") |
104 | (init/read-input)) |
105 |
|
106 | (fn init [] |
107 | (if (required-paths-missing?) |
108 | (init/prompt) |
109 | (print "The required directories and files already exist."))) |
110 |
|
111 | ;; --------------------------------------- |
112 | ;; serve |
113 | ;; --------------------------------------- |
114 | (fn serve [] |
115 | (if (required-paths-missing?) |
116 | (print-missing-files) |
117 | (if (build-directory-has-contents?) |
118 | (os.execute "python3 -m http.server 8000 --directory build/") |
119 | (do (print "Error: 'build/' directory has no contents.") |
120 | (print "Try running the following command first:") |
121 | (print " wg build"))))) |
122 |
|
123 | ;; --------------------------------------- |
124 | ;; clean |
125 | ;; --------------------------------------- |
126 | (fn clean/start [] |
127 | (print "Deleting contents of 'build/' directory...") |
128 | (ffs.path-delete (.. :build "/*")) |
129 | (print "Cleaning complete!")) |
130 |
|
131 | (fn clean/prompt [] |
132 | (print "Cleaning will delete everything in the 'build/' directory.") |
133 | (print "Do you want to continue? (y/n)") |
134 | (io.write "> ") |
135 | (let [input (io.read 1)] |
136 | (if (= input :y) |
137 | (clean/start) |
138 | (print "Cancelled the deletion of the 'build/' directory's contents.")))) |
139 |
|
140 | (fn clean [] |
141 | (if (required-paths-missing?) |
142 | (print-missing-files) |
143 | (if (build-directory-has-contents?) |
144 | (clean/prompt) |
145 | (print "'build/' directory empty. Nothing to clean.")))) |
146 |
|
147 | ;; --------------------------------------- |
148 | ;; build |
149 | ;; --------------------------------------- |
150 | ;; Note: source and destination should both be full paths starting from |
151 | ;; where wg.fnl is ran |
152 | (fn markdown->html [source-file destination-file] |
153 | (os.execute |
154 | (string.format "pandoc -s -c /style.css %s %s %s -o %s --shift-heading-level-by=1" |
155 | (. files-required :header) |
156 | source-file |
157 | (. files-required :footer) |
158 | destination-file))) |
159 |
|
160 | (fn build/convert [dir] |
161 | (each [_ path (ipairs (ffs.directory-contents dir))] |
162 | (let [source-path (.. dir "/" path)] |
163 | (if (and (ffs.file-exists? source-path) |
164 | (string.match source-path ".md")) |
165 | (let [destination-dir (-> source-path |
166 | (string.gsub "(.*/).*.md" "%1") ;; => convert/some/path/ |
167 | (string.gsub "^convert/" "build/")) ;; => build/some/path/ |
168 | destination-file (-> source-path |
169 | (string.gsub ".md" ".html") ;; => convert/some/path/file.html |
170 | (string.gsub "^convert/" "build/"))] ;; => build/some/path/file.html |
171 | (when (not (ffs.directory-exists? destination-dir)) |
172 | (ffs.directory-create destination-dir)) |
173 | (markdown->html source-path destination-file)) |
174 | (when (ffs.directory-exists? source-path) |
175 | (build/convert source-path)))))) |
176 |
|
177 | (fn build [] |
178 | (if (required-paths-missing?) |
179 | (print-missing-files) |
180 | (do ;; Copy paths |
181 | (if (= (length (ffs.directory-contents :copy)) 0) |
182 | (print "No directories or files found in the 'copy/' directory. Skipping...") |
183 | (do (print "Copying files in 'copy/' directory...") |
184 | (ffs.path-copy (.. :copy "/*") :build) |
185 | (print "Copying complete!"))) |
186 | ;; Convert paths |
187 | (if (= (length (ffs.directory-contents :convert)) 0) |
188 | (print "No directories or files found in the 'convert/' directory. Skipping...") |
189 | (do (print "Converting files in 'convert/' directory...") |
190 | (build/convert :convert) |
191 | (print "Conversion complete!")))))) |
192 |
|
193 | (fn repair/start [] |
194 | (each [_ dir (ipairs directories-required)] |
195 | (when (not (ffs.directory-exists? dir)) |
196 | (print-format "Creating '%s/'..." dir) |
197 | (ffs.directory-create dir))) |
198 | (each [_ file (ipairs [files-required.index |
199 | files-required.style |
200 | files-required.header |
201 | files-required.footer])] |
202 | (when (not (ffs.file-exists? file)) |
203 | (print-format "Creating '%s'..." file) |
204 | (ffs.file-create file) |
205 | (let [make-file (fn [file contents] |
206 | (ffs.file-write file contents :w))] |
207 | (match file |
208 | files-required.index (make-file files-required.index contents-index) |
209 | files-required.style (make-file files-required.style contents-style) |
210 | files-required.header (make-file files-required.header contents-header) |
211 | files-required.footer (make-file files-required.footer contents-footer))))) |
212 | (print "Repair complete!")) |
213 |
|
214 | (fn repair/read-input [] |
215 | (let [input (io.read 1)] |
216 | (if (= input :y) |
217 | (repair/start) |
218 | (print "Cancelled the repairs.")))) |
219 |
|
220 | (fn repair/prompt [] |
221 | (print "The following files or directories are missing:") |
222 | (each [_ dir (ipairs directories-required)] |
223 | (when (not (ffs.directory-exists? dir)) |
224 | (print-format " %s/" dir))) |
225 | (each [_ file (pairs files-required)] |
226 | (when (not (ffs.file-exists? file)) |
227 | (print-format " %s" file))) |
228 | (print "Do you want to create these files? (y/n)") |
229 | (io.write "> ") |
230 | (repair/read-input)) |
231 |
|
232 | (fn repair [] |
233 | (if (required-paths-missing?) |
234 | (repair/prompt) |
235 | (print "The required directories and files already exist."))) |
236 |
|
237 | (fn help [] |
238 | (print |
239 | (.. "wg\n" |
240 | " A static website generator written in Fennel.\n" |
241 | "\n" |
242 | "Author\n" |
243 | " Jesse Laprade (m455)\n" |
244 | "\n" |
245 | "License\n" |
246 | " AGPL3 (https://www.gnu.org/licenses/agpl-3.0.en.html)\n" |
247 | "\n" |
248 | "Source\n" |
249 | " https://git.m455.casa/m455/wg\n" |
250 | "\n" |
251 | "Commands\n" |
252 | " init\n" |
253 | " Creates required directories and files in the current directory.\n" |
254 | "\n" |
255 | " build\n" |
256 | " Recursively copies directories and files from the 'copy/'\n" |
257 | " directory into the 'build/' directory, preserving the directory\n" |
258 | " structure of the 'copy/' directory.\n" |
259 | "\n" |
260 | " Recursively converts Markdown files in the 'convert/' directory\n" |
261 | " to HTML files in the 'build/' directory, preserving the\n" |
262 | " directory structure of the 'convert/' directory.\n" |
263 | "\n" |
264 | " serve\n" |
265 | " Serves files in the 'build/' directory on port 8000, allowing\n" |
266 | " you to see how your website will look locally before it goes\n" |
267 | " live.\n" |
268 | "\n" |
269 | " clean\n" |
270 | " Deletes all contents of the 'build/' directory.\n" |
271 | "\n" |
272 | " repair\n" |
273 | " Looks for and creates missing files or directories.\n" |
274 | "\n" |
275 | " help\n" |
276 | " Displays this help message.\n" |
277 | "\n" |
278 | "Example usage\n" |
279 | " wg init\n" |
280 | " wg build\n" |
281 | " wg serve\n" |
282 | " wg clean\n" |
283 | " wg help\n"))) |
284 |
|
285 | ;; --------------------------------------- |
286 | ;; Arg parsing |
287 | ;; --------------------------------------- |
288 | (fn main [arg-tbl] |
289 | (match arg-tbl |
290 | [:init nil] (init) |
291 | [:build nil] (build) |
292 | [:serve nil] (serve) |
293 | [:clean nil] (clean) |
294 | [:repair nil] (repair) |
295 | [:help nil] (help) |
296 | _ (do (print "For help, type the following command:") |
297 | (print " wg help")))) |
298 |
|
299 | (main arg) |