Commit 5cbd0ab6 authored by Dave Cooper's avatar Dave Cooper

Support for content/*.lisp files in addition to content/*.html.

parent 821b70e4
......@@ -24,6 +24,10 @@ Load with this command:
(ql:quickload :cl-site)
To clean any old output/ directory:
(cl-site:make-clean)
To generate the site:
(cl-site::make-site)
......
......@@ -8,7 +8,7 @@
:author "C. Yang & Common-Lisp.Net maintainers"
:licence "TBD"
:description "Static site generator for common-lisp.net, written in CL"
:depends-on (:cl-mustache :plump :cl-markdown :closer-mop :cl-who :cl-yaml)
:depends-on (:cl-mustache :plump :markdown.cl :closer-mop :cl-who :cl-yaml)
:components ((:module source
:pathname ""
:serial t
......
(in-package :cl-site)
(in-package :cl-site-content)
(add-content
"about.html"
:title "Common-Lisp.net | About"
:html-string
(with-cl-who-string (:indent t)
(:h1 ((:i :class "far fa-question-circle")) " About Common-Lisp.net")
......
......@@ -24,15 +24,27 @@
(defparameter *PRIVATE-KEYS* '(:slug :content))
(defparameter *PAGES*
(defun populate-pages ()
(mapcar (lambda (p)
(list (cons :content (file-namestring p))
(cons :title (file-namestring p))))
(directory (make-pathname :defaults *PAGES-DIR*
:name :wild
:type "html")))
;;
;; At this point there could be duplicate, i.e. a .html and a .lisp file.
;;
(append
(directory (make-pathname :defaults *PAGES-DIR* :name :wild :type "html"))
(directory (make-pathname :defaults *PAGES-DIR* :name :wild :type "lisp")))))
(defparameter *PAGES* (populate-pages)
"Each page is an alist containing info to be sent to the template via the context.")
;; Initialize global context (will be appended to all individual page contexts)
;; Used to store things like stylesheets, etc...
(defparameter *GLOBAL-CONTEXT* ())
(defparameter *computed-page-content* nil
"Used as dynamically bound variable in preprocess-lisp-pages, to
capture output when compile/loading any lispy html content files
wrapped with calls to add-content. Global value should always be
nil.")
......@@ -6,3 +6,13 @@
(format t "Generating site in ~A.~%" *output-dir*)
(process-static)
(process-pages *pages*)))
(defun make-clean (&optional (output-dir *output-dir*))
(when (probe-file output-dir)
(uiop:delete-directory-tree
output-dir
:validate #'(lambda(dir)
(string-equal (first (last (pathname-directory dir)))
"output"))))
(ensure-directories-exist output-dir))
(defpackage #:cl-site
(:use :cl)
(:export
#:make-site))
(:export #:make-site #:make-clean #:add-content #:wmd :with-cl-who-string))
(defpackage #:cl-site-content
(:use :cl))
(:use :cl :cl-site :cl-who))
......@@ -41,7 +41,7 @@
(header content)
(split-content-from-header page-path)
(let* ((header-text (format nil "~{~A~%~}" header))
(content-text (format nil "~{~A~%~}" content))
(content-text (format nil "~{~A~%~}" (remove nil content)))
(parsed-header (yaml:parse header-text))
(page-context
(when parsed-header
......@@ -54,52 +54,62 @@
(mustache:render template-path (append (acons :page-content content-text page-context) *GLOBAL-CONTEXT*)
output-stream))))))
(defun process-pages (pages)
(format t "Processing pages...~%")
;;
;; Go through all the pages in *page-context* and look for
;; corresponding .lisp file. If it exists, it's assumed this is
;; using `add-content' (defined below) to generate an HTML string and
;; add it to the :content field of its entry in *page-context*. So
;; we compile/load this lisp file then write the content out to the
;; html file. Yes, this is turning into a goddamn Rube Goldberg
;; setup and at some point we will need to clean up and simplify
;; this whole process... But trying to get to where we can at least
;; experiment with using LHTML and markdown to author and maintain
;; page content.
;;
(dolist (page *page-context*)
(let* ((html-file (first page))
(lisp-file (probe-file (make-pathname :defaults *pages-dir*
:name (pathname-name html-file)
:type "lisp"))))
(when lisp-file
(format t "Compile/loading ~a and creating fresh ~a...~%" (file-namestring lisp-file) html-file)
(let ((fasl (compile-file lisp-file)))
(load fasl) (delete-file fasl))
(let ((content (page-content html-file)))
(with-open-file (out (merge-pathnames html-file *pages-dir*) :if-exists :supersede
:if-does-not-exist :create :direction :output)
(write-string content out))))))
(defun process-pages (pages)
(format t "Processing pages...~%")
(preprocess-lisp-pages pages)
(generate-news)
(let ((template-path (pathname (merge-pathnames *LAYOUT-DIR* *DEFAULT-PAGE-TEMPLATE*))))
(ensure-directories-exist *OUTPUT-DIR*)
(loop for page in pages
do
(format t "Rendering ~A~%" (cdr (assoc :content page)))
(render-page page template-path))
(setq pages (populate-pages))
(dolist (page pages)
(let ((page-pathname (merge-pathnames (page-content page) *pages-dir*)))
(when (string-equal (pathname-type page-pathname) "html")
(format t "Rendering ~A~%" (cdr (assoc :content page)))
(render-page page template-path)
(when (probe-file (make-pathname :defaults page-pathname :type "lisp"))
(format t "Deleting temporary generated ~a~%" (file-namestring page-pathname))
(delete-file page-pathname)
))))
(format t "Done.~%")))
(defun preprocess-lisp-pages (pages)
(dolist (page pages)
(let* ((lisp-file (page-content page)))
(when (string-equal (pathname-type lisp-file) "lisp")
(let ((html-file (make-pathname :defaults lisp-file :type "html")))
(format t "Compile/loading ~a and creating fresh ~a...~%"
(file-namestring lisp-file) html-file)
(let ((fasl (compile-file (merge-pathnames lisp-file *pages-dir*)))
*computed-page-content*)
(load fasl) (delete-file fasl)
(with-open-file (out (merge-pathnames html-file *pages-dir*) :if-exists :supersede
:if-does-not-exist :create :direction :output)
(format out "title: ~a~%---~%" (getf *computed-page-content* :title))
(write-string (getf *computed-page-content* :html-string) out))))))))
;; Process news
(defparameter +edit-news-link+ "https://gitlab.common-lisp.net/clo/cl-site/edit/master/content/news.md")
;;
;; FLAG -- If news.html is converted to news.lisp (a la about.lisp)
;; then the following only will have to deal with generating the
;; newsbox. The news.lisp would have to include the hardcoded HTML
;; which is injected below as well.
;;
(defun generate-news ()
(let ((html
(with-output-to-string (ss)
(cl-markdown:markdown (merge-pathnames "news.md" *PAGES-DIR*) :stream ss))))
(markdown:parse-file (merge-pathnames "news.md" *PAGES-DIR*))))
(with-open-file (f (merge-pathnames "news.html" *PAGES-DIR*)
:direction :output
:if-does-not-exist :create
......@@ -191,13 +201,20 @@ of writing to a stream."
"Form suitable for inclusion in cl-who LHTML context. Returns an
`str` form after passing it through cl-markdown's markdown processor.
"
`(cl-who:str
(with-output-to-string (ss)
(cl-markdown:markdown ,string :stream ss))))
;;
;; FLAG -- should we be checking for loaded markdown package at compile-time instead?
;;
`(cl-who:str (cond ((find-package :markdown.cl)
(funcall (read-from-string "markdown.cl:parse") ,string))
((find-package :cl-markdown)
(with-output-to-string (ss)
(funcall (read-from-string "cl-markdown:markdown") ,string :stream ss))))))
(defun page-content (page) (rest (assoc :content page)))
(defun add-content (&key title html-string)
(setq *computed-page-content* (list :title title :html-string html-string)))
(defun add-content (page-name content-string)
(pushnew (cons :content content-string)
(first (rest (assoc page-name *page-context* :test #'string-equal)))))
(defun page-content (page-name)
(rest (assoc :content (page-context page-name))))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment