My nest. My website
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.
 
 
 
 
 
 

380 lines
12 KiB

  1. :;exec cask emacs --no-site-file --no-site-lisp --batch -L ./ -l "$0" -f main "$(cd "$(dirname "$0")/." >/dev/null 2>&1 ; pwd -P)" "$@"
  2. ;;; build.el --- The build script of my personal website
  3. ;;; Version: 0.1.0
  4. ;;; Package-Version 0.1.0
  5. ;;; Commentary:
  6. ;;; Code:
  7. (require 'org)
  8. (require 'seq)
  9. (require 'ox-html)
  10. (require 'ox-latex)
  11. (require 'ox-publish)
  12. (require 'dracula-theme)
  13. (require 'lisp/ox-template)
  14. (require 'lisp/utils)
  15. (setq debug-on-error t)
  16. (defvar author-name "Sameer Rahmani"
  17. "Set this variable to your fullname.")
  18. (defvar author-email "lxsameer@gmail.com"
  19. "Set this varibale to your email address.")
  20. (defvar project-root nil
  21. "Root directory of the website source code.")
  22. (defvar orgs-files-dir "/orgs/"
  23. "Path to the directory containing all the renderable org files.")
  24. (defun from-root (path)
  25. "Return the full path of the given PATH in the project root."
  26. (concat project-root path))
  27. (defun all-org-files ()
  28. "Return a list of all the org files in the orgs directory."
  29. (mapcar (lambda (x) x)
  30. (split-string
  31. (shell-command-to-string (format "find %s -iname \"*.org\"" org-directory))
  32. "\n" t)))
  33. (comment
  34. (all-org-files))
  35. (defun get-all-tags ()
  36. "Return a list of all the tags in the org files."
  37. (seq-reduce
  38. ;; all-tags is in (tags . tags->files) form
  39. (lambda (all-tags file)
  40. (if (get-file-global-props file "PAGE")
  41. ;; Ignore pages
  42. all-tags
  43. (with-temp-buffer
  44. (insert-file-contents file)
  45. (let ((tags (mapcar #'car (org-get-buffer-tags))))
  46. (seq-reduce
  47. (lambda (result tag)
  48. (let ((tag-list (car result))
  49. (tag->file (cdr result)))
  50. (cons
  51. ;; Tag list
  52. (if (member tag tag-list) tag-list (sort (cons tag tag-list) 'string<))
  53. ;; tag->file
  54. (cons (cons tag
  55. ;; Current value of the the given tag (all the files
  56. ;; that contain that tag)
  57. (append (list file) (cdr (assoc tag tag->file))))
  58. tag->file))))
  59. tags
  60. all-tags)))))
  61. (all-org-files)
  62. '()))
  63. (defun get-all-posts ()
  64. "Return all the post org files.
  65. Not pages."
  66. (let ((files (all-org-files)))
  67. (seq-reduce
  68. (lambda (result file)
  69. (let ((is-page? (string= (get-file-global-props file "PAGE") "true")))
  70. (if (not is-page?)
  71. ;; It's a post
  72. (cons
  73. (list
  74. ;; This is not effecient since we parse the file
  75. ;; on each query, but who cares :D ?
  76. (->epoch (get-file-global-props file "DATE"))
  77. (get-file-global-props file "TITLE")
  78. (replace-regexp-in-string "\\.org" ".html"
  79. (file-name-nondirectory file)))
  80. result)
  81. result)))
  82. files
  83. '())))
  84. (defun get-all-sorted-posts ()
  85. "Return all posts in sorted order."
  86. (sort
  87. (get-all-posts)
  88. (lambda (x y) (> (car x) (car y)))))
  89. (comment
  90. (get-all-sorted-posts))
  91. (defun get-all-categories ()
  92. "Return all the categories of the org files."
  93. (seq-reduce
  94. ;; all-cats is in (cats . cat->files) form
  95. (lambda (all-cats file)
  96. (let ((is-page? (string= (get-file-global-props file "PAGE") "true"))
  97. (cat (get-file-global-props file "CATEGORY"))
  98. (cat-list (car all-cats))
  99. (cat->file (cdr all-cats)))
  100. (if (not is-page?)
  101. (cons
  102. ;; Category list
  103. (if (member cat cat-list) cat-list (sort (cons cat cat-list) 'string<))
  104. ;; cat->file
  105. (cons (cons cat
  106. ;; Current value of the the given cat (all the files
  107. ;; under that category)
  108. (append (list file) (cdr (assoc cat cat->file))))
  109. cat->file))
  110. all-cats)))
  111. (all-org-files)
  112. '()))
  113. (comment
  114. (get-file-global-props "./orgs/essays/serene-blah.org" "CATEGORY")
  115. (get-all-categories))
  116. (defun extra-headers ()
  117. "Retun a list of extra header html tags."
  118. (concat
  119. "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"))
  120. (defun category-org-list ()
  121. "Return a list of links to the categories in org format."
  122. (let ((categories (get-all-categories)))
  123. (mapconcat
  124. (lambda (cat)
  125. (let ((count (length (cdr (assoc cat (cdr categories))))))
  126. (format " - [[./%s.html][%s(%s)]]" cat cat count)))
  127. (car categories)
  128. "\n")))
  129. (defun tags-org-list ()
  130. "Return a list of links to the tags in org format."
  131. (let ((tags (get-all-tags)))
  132. (mapconcat
  133. (lambda (tag)
  134. (let ((count (length (cdr (assoc tag (cdr tags))))))
  135. (format " - [[./%s.html][%s(%s)]]" tag tag count)))
  136. (car tags)
  137. "\n")))
  138. (defun latest-org-list (base-url)
  139. "Return a list of links (using BASE-URL) to the tags in org format."
  140. (let ((posts (get-all-sorted-posts)))
  141. (mapconcat
  142. (lambda (post)
  143. (format " - [[%s/essays/%s][%s]]" base-url (nth 2 post) (nth 1 post)))
  144. posts
  145. "\n")))
  146. (comment
  147. (latest-org-list))
  148. (defun pair-file-with-date (file)
  149. "Return a pair for the given FILE with date as car and file as cdr."
  150. (cons
  151. (->epoch (get-file-global-props file "DATE"))
  152. file))
  153. (defun create-tag-pages (project-dir)
  154. "Create all the tag files in the PROJECT-DIR."
  155. (let ((tags (get-all-tags)))
  156. (mapcar
  157. (lambda (tag)
  158. (let ((out (format "%s/orgs/tags/%s.org" project-dir tag))
  159. (files (cdr (assoc tag (cdr tags)))))
  160. (copy-template
  161. (format "%s/templates/links_template.org" project-dir)
  162. out
  163. (mapconcat
  164. (lambda (file-pair)
  165. (format "- [[file:..%s][%s]]"
  166. (replace-regexp-in-string (regexp-quote (from-root "/orgs")) "" (cdr file-pair) nil 'literal)
  167. (get-file-global-props (cdr file-pair) "TITLE")))
  168. (sort
  169. (mapcar #'pair-file-with-date files)
  170. (lambda (x y) (> (car x) (car y))))
  171. "\n")
  172. tag)))
  173. (car tags))))
  174. (defun create-category-pages (project-dir)
  175. "Create all the category files in the PROJECT-DIR."
  176. (let ((tags (get-all-categories)))
  177. (mapcar
  178. (lambda (tag)
  179. (let ((out (format "%s/orgs/categories/%s.org" project-dir tag))
  180. (files (cdr (assoc tag (cdr tags)))))
  181. (copy-template
  182. (format "%s/templates/links_template.org" project-dir)
  183. out
  184. (mapconcat
  185. (lambda (file-pair)
  186. (format "- [[file:..%s][%s]]"
  187. (replace-regexp-in-string (regexp-quote (from-root "/orgs")) "" (cdr file-pair) nil 'literal)
  188. (get-file-global-props (cdr file-pair) "TITLE")))
  189. (sort
  190. (mapcar #'pair-file-with-date files)
  191. (lambda (x y) (> (car x) (car y))))
  192. "\n")
  193. tag)))
  194. (car tags))))
  195. (defun main ()
  196. "The entry point to the build script."
  197. ;; Setup the emacs theme so htmlize can actually setup
  198. ;; the code highlighter
  199. (load-theme 'dracula t)
  200. (enable-theme 'dracula)
  201. (setq project-root (car command-line-args-left))
  202. ;; We will use the org-agenda to extract all the tags
  203. (setq org-directory (from-root orgs-files-dir))
  204. (setq org-agenda-files (all-org-files))
  205. (setf user-full-name author-name)
  206. (setf user-mail-address author-email)
  207. ;; Disable default header links (top, next)
  208. (setf org-html-home/up-format "")
  209. (setf org-html-link-up "")
  210. (setf org-html-link-home "")
  211. (setf org-html-scripts "")
  212. ;; (org-babel-do-load-languages
  213. ;; 'org-babel-load-languages
  214. ;; '(
  215. ;; (emacs-lisp . t)
  216. ;; (org . t)
  217. ;; (shell . t)
  218. ;; (C . t)
  219. ;; (python . t)
  220. ;; (clojure .t)
  221. ;; (lisp . t)
  222. ;; (js . t)
  223. ;; (awk . t)))
  224. ;; ;; Never export the code block evaluation
  225. ;; (setq org-babel-default-header-args '((:eval . "never-export")))
  226. ;; (setq org-src-fontify-natively t)
  227. (let ((build-dir (from-root "/build/"))
  228. (base-url (if (prod-p) "https://lxsameer.com" "http://localhost:3003")))
  229. (copy-template (from-root "/templates/index.org")
  230. (from-root "/orgs/index.org")
  231. (latest-org-list base-url))
  232. (copy-template (from-root "/templates/categories.org")
  233. (from-root "/orgs/categories/index.org")
  234. (category-org-list))
  235. (copy-template (from-root "/templates/tags.org")
  236. (from-root "/orgs/tags/index.org")
  237. (tags-org-list))
  238. (create-tag-pages project-root)
  239. (create-category-pages project-root)
  240. (setq org-html-preamble #'preamble-fn)
  241. (setq org-html-htmlize-output-type nil)
  242. (setq org-latex-listings t)
  243. (setq org-publish-project-alist
  244. `(("lxsameer.com"
  245. :base-directory ,(from-root "/orgs")
  246. :root-directory ,project-root
  247. :recursive t
  248. :base-extension "org"
  249. :publishing-directory ,build-dir
  250. ;; Exclude the blog archive index autogenerated below
  251. ;; Note that the regexp is relative to :base-directory
  252. ;; :exclude "^index.org"
  253. :section-numbers nil
  254. :with-author t
  255. :with-drawers t
  256. :html-format-drawer-function custom-drawer-format
  257. :with-properties t
  258. :with-tags t
  259. :with-timestamps t
  260. :with-toc nil
  261. :base-url ,base-url
  262. :html-link-home "/"
  263. :html-head-extra ,(extra-headers)
  264. :html-template ,(from-root "/templates/blog.html")
  265. :html-page-preamble-template ,(from-root "/templates/page-preamble.html")
  266. :html-post-preabmle-template ,(from-root "/templates/post-preamble.html")
  267. :html-tags-template ,(from-root "/templates/tags.html")
  268. :publishing-function org-html-publish-to-templated-html
  269. :auto-sitemap t
  270. :htmlized-source nil
  271. :sitemap-folders ignore
  272. :sitemap-style list
  273. :sitemap-title "lxsameer's nest"
  274. :sitemap-filename "sitemap.inc"
  275. :sitemap-sort-files anti-chronologically
  276. :html-format-headline-function headline-format
  277. :makeindex nil)
  278. ("org->html"
  279. :base-directory ,(from-root "/orgs")
  280. :base-extension "org"
  281. :publishing-directory ,build-dir
  282. :recursive t
  283. :publishing-function org-html-publish-to-html
  284. :headline-levels 4
  285. ;; :html-preamble ,(use-html "templates/header.html")
  286. ;; :html-postamble ,(use-html "templates/footer.html")
  287. :html-link-home "/"
  288. :html-head-include-default-style nil
  289. :html-head-include-scripts nil
  290. :html-head-extra ,(extra-headers)
  291. :makeindex nil)
  292. ("pdfs"
  293. :base-directory ,(from-root "/orgs/essays")
  294. :root-directory ,project-root
  295. ;;:publishing-directory ,(concat build-dir "/essays/")
  296. :recursive t
  297. :base-extension "org"
  298. :publishing-directory ,build-dir
  299. :publishing-function org-latex-publish-to-pdf)
  300. ("statics"
  301. :base-directory ,project-root
  302. :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|svg"
  303. :publishing-directory ,build-dir
  304. :recursive t
  305. :publishing-function org-publish-attachment)
  306. ("build" :components ("lxsameer.com" "statics"))))
  307. (org-publish-project "build" t nil)
  308. (message "Build complete.")))
  309. (provide 'build)
  310. ;; Local Variables:
  311. ;; mode: emacs-lisp
  312. ;; End:
  313. ;;; build.el ends here