:;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 "")) (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