-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.lisp
147 lines (126 loc) · 5.08 KB
/
main.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
(defpackage die-trying
(:use :cl)
(:export :start-dev :process :watch))
(in-package #:die-trying)
;;; Utils
(defun mapc-walk-directory (fn dir)
"Walk a directory, `dir'` and run `fn' on all subdirectories and files."
(dolist (entry (cl-fad:list-directory dir))
(when (cl-fad:directory-pathname-p entry)
(mapc-walk-directory fn entry))
(funcall fn entry)))
(defun vector-empty-p (vec)
(unless (vector-to-list vec) t))
(defun vector-to-list (vec)
(coerce vec 'list))
(defun copy-file (path &optional (out (get-out-path path)))
(unless (cl-fad:directory-exists-p out)
(cl-fad:copy-file path (get-out-path path) :overwrite t)))
(defun get-out-path (file)
(let* ((file-part (cadr (str:split *input-dir* (namestring file))))
(out (str:concat *output-dir* file-part)))
(ensure-directories-exist out)
out))
(defun categorize-file (path)
(cond
((str:starts-with-p ".#" (pathname-name path)) nil)
((not (equal (pathname-type path) "html")) 'asset)
(t (let ((dom (lquery:$ (initialize path))))
(if (vector-empty-p (lquery:$ dom "html"))
'fragment
'template)))))
(defun path-to-dom (path &optional (fragment nil))
(if fragment
(lquery:$ (initialize path) (children))
(lquery:$ (initialize path))))
(defun build-fragment-objects (paths)
(mapcar (lambda (path)
(let ((dom (path-to-dom path t))
(tag (pathname-name path)))
(list :dom dom :tag tag)))
paths))
(defun find-dependencies (fragments)
(let (updated-fragments)
(mapcar (lambda (dom-plist)
(let ((dependencies nil)
(dom (getf dom-plist ':dom)))
(mapcar (lambda (fragment)
(let ((tag (getf fragment ':tag)))
(when (not (vector-empty-p (lquery:$ dom tag)))
(setf dependencies (cons tag dependencies)))))
fragments)
(setf updated-fragments
(cons (list :tag (getf dom-plist ':tag)
:dom dom
:depends-on dependencies)
updated-fragments))))
fragments)
updated-fragments))
(defun node-children-to-str (node)
(car (vector-to-list (lquery:$ node (text)))))
(defun expand (node fragments &optional (evaled ""))
(mapcar (lambda (fragment)
(let ((tag (getf fragment ':tag))
(el (getf fragment ':dom)))
(lquery:$ node tag (prepend el))))
fragments)
node)
;;; Processors
(defun process-input-files (dir)
(let (templates fragments)
(mapc-walk-directory (lambda (path)
(let ((category (categorize-file path)))
(cond ((equal category 'template) (setf templates (cons path templates)))
((equal category 'fragment) (setf fragments (cons path fragments)))
((equal category 'asset) (copy-file path))
(t) (nil))))
dir)
(list templates fragments)))
(defun process-templates (templates fragments)
(mapcar (lambda (path)
(let ((dom (path-to-dom path))
(out (get-out-path path)))
(lquery:$ dom (expand fragments) (write-to-file out))))
templates))
(defun process-fragments (fragment-paths)
(let* ((fragments (build-fragment-objects fragment-paths))
(fragments (find-dependencies fragments))
(expanded-fragments nil))
(mapcar (lambda (fragment)
(setf expanded-fragments
(cons (list :tag (getf fragment ':tag)
:dom (expand (getf fragment ':dom) fragments))
expanded-fragments)))
fragments)
expanded-fragments))
(defun process (&optional (in "www/") (out "out/"))
(defparameter *input-dir* in)
(defparameter *output-dir* out)
(let* ((x (process-input-files in))
(fragments (process-fragments (cadr x))))
(process-templates (car x) fragments)))
;; Dev server
(defparameter *acceptor* nil)
(defparameter *port* 4321)
(defun file-watcher (path func)
"Starts a file-notify watcher in a thread to watch `path' and run `func' when a file changes."
(format t "Starting file watcher on ~a~%" path)
(org.shirakumo.file-notify:watch path)
(org.shirakumo.file-notify:with-events (file change :timeout t)
(when (not (str:starts-with-p ".#" (pathname-name file)))
(format t "~a changed~%" file)
(funcall func file change))))
(defun watch ()
(file-watcher "www/" (lambda (file change)
(declare (ignore file change))
(process))))
(defun serve ()
(setf *acceptor* (make-instance 'hunchentoot:easy-acceptor
:port *port*
:document-root *output-dir*))
(format t "Starting development server on ~a~%." *port*)
(hunchentoot:start *acceptor*))
(defun start-dev ()
(process)
(serve)
(watch))