You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
12 KiB
379 lines
12 KiB
:;exec cask emacs --no-site-file --no-site-lisp --batch -L ./ -l "$0" -f main "$(cd "$(dirname "$0")/." >/dev/null 2>&1 ; pwd -P)" "$@" |
|
;;; build.el --- The build script of my personal website |
|
;;; Version: 0.1.0 |
|
;;; Package-Version 0.1.0 |
|
;;; Commentary: |
|
;;; Code: |
|
(require 'org) |
|
(require 'seq) |
|
(require 'ox-html) |
|
(require 'ox-latex) |
|
(require 'ox-publish) |
|
(require 'dracula-theme) |
|
(require 'lisp/ox-template) |
|
(require 'lisp/utils) |
|
|
|
|
|
(setq debug-on-error t) |
|
|
|
(defvar author-name "Sameer Rahmani" |
|
"Set this variable to your fullname.") |
|
|
|
(defvar author-email "lxsameer@gmail.com" |
|
"Set this varibale to your email address.") |
|
|
|
(defvar project-root nil |
|
"Root directory of the website source code.") |
|
|
|
|
|
(defvar orgs-files-dir "/orgs/" |
|
"Path to the directory containing all the renderable org files.") |
|
|
|
|
|
(defun from-root (path) |
|
"Return the full path of the given PATH in the project root." |
|
(concat project-root path)) |
|
|
|
|
|
(defun all-org-files () |
|
"Return a list of all the org files in the orgs directory." |
|
(mapcar (lambda (x) x) |
|
(split-string |
|
(shell-command-to-string (format "find %s -iname \"*.org\"" org-directory)) |
|
"\n" t))) |
|
|
|
(comment |
|
(all-org-files)) |
|
|
|
|
|
(defun get-all-tags () |
|
"Return a list of all the tags in the org files." |
|
(seq-reduce |
|
;; all-tags is in (tags . tags->files) form |
|
(lambda (all-tags file) |
|
(if (get-file-global-props file "PAGE") |
|
;; Ignore pages |
|
all-tags |
|
(with-temp-buffer |
|
(insert-file-contents file) |
|
(let ((tags (mapcar #'car (org-get-buffer-tags)))) |
|
(seq-reduce |
|
(lambda (result tag) |
|
(let ((tag-list (car result)) |
|
(tag->file (cdr result))) |
|
(cons |
|
;; Tag list |
|
(if (member tag tag-list) tag-list (sort (cons tag tag-list) 'string<)) |
|
;; tag->file |
|
(cons (cons tag |
|
;; Current value of the the given tag (all the files |
|
;; that contain that tag) |
|
(append (list file) (cdr (assoc tag tag->file)))) |
|
tag->file)))) |
|
tags |
|
all-tags))))) |
|
(all-org-files) |
|
'())) |
|
|
|
|
|
(defun get-all-posts () |
|
"Return all the post org files. |
|
Not pages." |
|
(let ((files (all-org-files))) |
|
(seq-reduce |
|
(lambda (result file) |
|
(let ((is-page? (string= (get-file-global-props file "PAGE") "true"))) |
|
(if (not is-page?) |
|
;; It's a post |
|
(cons |
|
(list |
|
;; This is not effecient since we parse the file |
|
;; on each query, but who cares :D ? |
|
(->epoch (get-file-global-props file "DATE")) |
|
(get-file-global-props file "TITLE") |
|
(replace-regexp-in-string "\\.org" ".html" |
|
(file-name-nondirectory file))) |
|
result) |
|
result))) |
|
files |
|
'()))) |
|
|
|
|
|
(defun get-all-sorted-posts () |
|
"Return all posts in sorted order." |
|
(sort |
|
(get-all-posts) |
|
(lambda (x y) (> (car x) (car y))))) |
|
|
|
|
|
(comment |
|
(get-all-sorted-posts)) |
|
|
|
|
|
(defun get-all-categories () |
|
"Return all the categories of the org files." |
|
(seq-reduce |
|
;; all-cats is in (cats . cat->files) form |
|
(lambda (all-cats file) |
|
(let ((is-page? (string= (get-file-global-props file "PAGE") "true")) |
|
(cat (get-file-global-props file "CATEGORY")) |
|
(cat-list (car all-cats)) |
|
(cat->file (cdr all-cats))) |
|
|
|
(if (not is-page?) |
|
(cons |
|
;; Category list |
|
(if (member cat cat-list) cat-list (sort (cons cat cat-list) 'string<)) |
|
;; cat->file |
|
(cons (cons cat |
|
;; Current value of the the given cat (all the files |
|
;; under that category) |
|
(append (list file) (cdr (assoc cat cat->file)))) |
|
cat->file)) |
|
all-cats))) |
|
(all-org-files) |
|
'())) |
|
|
|
|
|
(comment |
|
(get-file-global-props "./orgs/essays/serene-blah.org" "CATEGORY") |
|
(get-all-categories)) |
|
|
|
|
|
(defun extra-headers () |
|
"Retun a list of extra header html tags." |
|
(concat |
|
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">")) |
|
|
|
|
|
(defun category-org-list () |
|
"Return a list of links to the categories in org format." |
|
(let ((categories (get-all-categories))) |
|
(mapconcat |
|
(lambda (cat) |
|
(let ((count (length (cdr (assoc cat (cdr categories)))))) |
|
(format " - [[./%s.html][%s(%s)]]" cat cat count))) |
|
(car categories) |
|
"\n"))) |
|
|
|
|
|
(defun tags-org-list () |
|
"Return a list of links to the tags in org format." |
|
(let ((tags (get-all-tags))) |
|
(mapconcat |
|
(lambda (tag) |
|
(let ((count (length (cdr (assoc tag (cdr tags)))))) |
|
(format " - [[./%s.html][%s(%s)]]" tag tag count))) |
|
(car tags) |
|
"\n"))) |
|
|
|
|
|
(defun latest-org-list (base-url) |
|
"Return a list of links (using BASE-URL) to the tags in org format." |
|
(let ((posts (get-all-sorted-posts))) |
|
(mapconcat |
|
(lambda (post) |
|
(format " - [[%s/essays/%s][%s]]" base-url (nth 2 post) (nth 1 post))) |
|
posts |
|
"\n"))) |
|
|
|
|
|
(comment |
|
(latest-org-list)) |
|
|
|
|
|
(defun pair-file-with-date (file) |
|
"Return a pair for the given FILE with date as car and file as cdr." |
|
(cons |
|
(->epoch (get-file-global-props file "DATE")) |
|
file)) |
|
|
|
|
|
(defun create-tag-pages (project-dir) |
|
"Create all the tag files in the PROJECT-DIR." |
|
(let ((tags (get-all-tags))) |
|
(mapcar |
|
(lambda (tag) |
|
(let ((out (format "%s/orgs/tags/%s.org" project-dir tag)) |
|
(files (cdr (assoc tag (cdr tags))))) |
|
(copy-template |
|
(format "%s/templates/links_template.org" project-dir) |
|
out |
|
(mapconcat |
|
(lambda (file-pair) |
|
(format "- [[file:..%s][%s]]" |
|
(replace-regexp-in-string (regexp-quote (from-root "/orgs")) "" (cdr file-pair) nil 'literal) |
|
(get-file-global-props (cdr file-pair) "TITLE"))) |
|
|
|
(sort |
|
(mapcar #'pair-file-with-date files) |
|
(lambda (x y) (> (car x) (car y)))) |
|
"\n") |
|
tag))) |
|
(car tags)))) |
|
|
|
|
|
(defun create-category-pages (project-dir) |
|
"Create all the category files in the PROJECT-DIR." |
|
(let ((tags (get-all-categories))) |
|
(mapcar |
|
(lambda (tag) |
|
(let ((out (format "%s/orgs/categories/%s.org" project-dir tag)) |
|
(files (cdr (assoc tag (cdr tags))))) |
|
(copy-template |
|
(format "%s/templates/links_template.org" project-dir) |
|
out |
|
(mapconcat |
|
(lambda (file-pair) |
|
(format "- [[file:..%s][%s]]" |
|
(replace-regexp-in-string (regexp-quote (from-root "/orgs")) "" (cdr file-pair) nil 'literal) |
|
(get-file-global-props (cdr file-pair) "TITLE"))) |
|
|
|
(sort |
|
(mapcar #'pair-file-with-date files) |
|
(lambda (x y) (> (car x) (car y)))) |
|
"\n") |
|
tag))) |
|
(car tags)))) |
|
|
|
|
|
(defun main () |
|
"The entry point to the build script." |
|
|
|
;; Setup the emacs theme so htmlize can actually setup |
|
;; the code highlighter |
|
(load-theme 'dracula t) |
|
(enable-theme 'dracula) |
|
|
|
|
|
(setq project-root (car command-line-args-left)) |
|
|
|
;; We will use the org-agenda to extract all the tags |
|
(setq org-directory (from-root orgs-files-dir)) |
|
(setq org-agenda-files (all-org-files)) |
|
|
|
(setf user-full-name author-name) |
|
(setf user-mail-address author-email) |
|
|
|
;; Disable default header links (top, next) |
|
(setf org-html-home/up-format "") |
|
(setf org-html-link-up "") |
|
(setf org-html-link-home "") |
|
(setf org-html-scripts "") |
|
|
|
;; (org-babel-do-load-languages |
|
;; 'org-babel-load-languages |
|
;; '( |
|
;; (emacs-lisp . t) |
|
;; (org . t) |
|
;; (shell . t) |
|
;; (C . t) |
|
;; (python . t) |
|
;; (clojure .t) |
|
;; (lisp . t) |
|
;; (js . t) |
|
;; (awk . t))) |
|
|
|
;; ;; Never export the code block evaluation |
|
;; (setq org-babel-default-header-args '((:eval . "never-export"))) |
|
;; (setq org-src-fontify-natively t) |
|
|
|
(let ((build-dir (from-root "/build/")) |
|
(base-url (if (prod-p) "https://lxsameer.com" "http://localhost:3003"))) |
|
(copy-template (from-root "/templates/index.org") |
|
(from-root "/orgs/index.org") |
|
(latest-org-list base-url)) |
|
|
|
(copy-template (from-root "/templates/categories.org") |
|
(from-root "/orgs/categories/index.org") |
|
(category-org-list)) |
|
|
|
(copy-template (from-root "/templates/tags.org") |
|
(from-root "/orgs/tags/index.org") |
|
(tags-org-list)) |
|
|
|
(create-tag-pages project-root) |
|
(create-category-pages project-root) |
|
|
|
(setq org-html-preamble #'preamble-fn) |
|
(setq org-html-htmlize-output-type nil) |
|
(setq org-latex-listings t) |
|
|
|
(setq org-publish-project-alist |
|
`(("lxsameer.com" |
|
:base-directory ,(from-root "/orgs") |
|
:root-directory ,project-root |
|
:recursive t |
|
:base-extension "org" |
|
:publishing-directory ,build-dir |
|
;; Exclude the blog archive index autogenerated below |
|
;; Note that the regexp is relative to :base-directory |
|
;; :exclude "^index.org" |
|
:section-numbers nil |
|
:with-author t |
|
:with-drawers t |
|
:html-format-drawer-function custom-drawer-format |
|
:with-properties t |
|
:with-tags t |
|
:with-timestamps t |
|
:with-toc nil |
|
:base-url ,base-url |
|
:html-link-home "/" |
|
:html-head-extra ,(extra-headers) |
|
:html-template ,(from-root "/templates/blog.html") |
|
:html-page-preamble-template ,(from-root "/templates/page-preamble.html") |
|
:html-post-preabmle-template ,(from-root "/templates/post-preamble.html") |
|
:html-tags-template ,(from-root "/templates/tags.html") |
|
:publishing-function org-html-publish-to-templated-html |
|
:auto-sitemap t |
|
:htmlized-source nil |
|
:sitemap-folders ignore |
|
:sitemap-style list |
|
:sitemap-title "lxsameer's nest" |
|
:sitemap-filename "sitemap.inc" |
|
:sitemap-sort-files anti-chronologically |
|
:html-format-headline-function headline-format |
|
:makeindex nil) |
|
|
|
("org->html" |
|
:base-directory ,(from-root "/orgs") |
|
:base-extension "org" |
|
:publishing-directory ,build-dir |
|
:recursive t |
|
:publishing-function org-html-publish-to-html |
|
:headline-levels 4 |
|
;; :html-preamble ,(use-html "templates/header.html") |
|
;; :html-postamble ,(use-html "templates/footer.html") |
|
:html-link-home "/" |
|
:html-head-include-default-style nil |
|
:html-head-include-scripts nil |
|
:html-head-extra ,(extra-headers) |
|
:makeindex nil) |
|
|
|
("pdfs" |
|
:base-directory ,(from-root "/orgs/essays") |
|
:root-directory ,project-root |
|
;;:publishing-directory ,(concat build-dir "/essays/") |
|
:recursive t |
|
:base-extension "org" |
|
:publishing-directory ,build-dir |
|
:publishing-function org-latex-publish-to-pdf) |
|
|
|
|
|
("statics" |
|
:base-directory ,project-root |
|
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|svg" |
|
:publishing-directory ,build-dir |
|
:recursive t |
|
:publishing-function org-publish-attachment) |
|
("build" :components ("lxsameer.com" "statics")))) |
|
|
|
(org-publish-project "build" t nil) |
|
(message "Build complete."))) |
|
|
|
|
|
(provide 'build) |
|
;; Local Variables: |
|
;; mode: emacs-lisp |
|
;; End: |
|
;;; build.el ends here
|
|
|