(in-package #:tempest) (defun read-tempest-string (filename) "Read file content as string. If it's not a .lisp file and does not have tempest comment at top, return NIL." (let ((content (uiop:read-file-string filename))) (if (str:starts-with-p ";;;; TEMPEST" content) content NIL))) (defun compile-to-html (tempest-string) "Compiles spinneret string to HTML string. Redirects to standard output." (let ((*read-eval* nil)) (-> (concatenate 'string "(spinneret:with-html " tempest-string ")") read-from-string eval))) (defun build-template (filename) (let ((raw (read-tempest-string filename))) (if raw (let ((target-file (str:replace-all ".lisp" ".html" filename))) (with-open-file (*standard-output* target-file :direction :output :if-exists :supersede :if-does-not-exist :create) (compile-to-html raw) target-file)) NIL))) (defun build-template-and-print (name) (let ((built (build-template name))) (if built (format t "built ~a~%" built) (format t "ignore ~a, not a valid tempest file.~%" name)))) (defun build (dir) (cl-fad:walk-directory dir (lambda (path-name) (let ((name (namestring path-name))) (when (str:ends-with-p ".lisp" name :ignore-case T) (build-template-and-print name)))))) ;; TODO handle frequent/simultaneous events, only build once ;; TODO handle :MOVE event, delete old one and create new one ;; TODO handle :DELETE event, delete template as well (defun watch (dir) (file-notify:watch dir) (file-notify:with-events (file change :timeout T) (when (and (str:ends-with-p ".lisp" file :ignore-case T) (or (eql change :MODIFY) (eql change :CREATE))) (let ((built-file (build-template file))) (if built-file (format t "Built ~a, because of ~a~%" built-file change) nil))))) (defun run (subcommand &rest args) (cond ((equal subcommand "build") (mapcar 'build (car args))) ((equal subcommand "build-each") (mapcar 'build-template-and-print (car args))) ;; TODO support watching multiple dirs ((equal subcommand "watch") (watch (car (car args)))))) (defun main () (let ((cli-args (cdr sb-ext:*posix-argv*))) (run (car cli-args) (cdr cli-args))))