1. Publish
(defun jdb/org-html-postamble (plist) (format "Last update: %s" (format-time-string "%d %b %Y"))) (setq org-html-postamble 'jdb/org-html-postamble) (setq org-html-doctype "html5") (setq org-html-html5-fancy t) (defun jdb/html-publish-to-html (plist filename pub-dir) "Publish an org file to HTML. FILENAME is the filename of the Org file to be published. PLIST is the property list for the given project. PUB-DIR is the publishing directory. Return output file name." (org-publish-org-to 'jdb/html filename (concat (when (> (length org-html-extension) 0) ".") (or (plist-get plist :html-extension) org-html-extension "html")) plist pub-dir)) (setq org-publish-project-alist '(("blog" :auto-sitemap t :sitemap-filename "index.org" :sitemap-title "Blog" :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"../reset.css\" />" :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"../stylesheet.css\" />" :base-directory "./blog" :language "en" :publishing-function jdb/html-publish-to-html :publishing-directory "./blog" :section-numbers t :with-toc t))) (jdb/export-to-html) (org-publish "blog" t)
1.1. Work around hardcoded strings
language: en
#+name org-export-dictionary
(defconst org-export-dictionary '(("Table of Contents" ("en" :html "Table of contents" :utf-8 "Table of contents"))))
1.2. Use HTML5
From HTML Doctypes.
Org’s HTML exporter does not by default enable new block elements introduced with the HTML5 standard. To enable them, set org-html-html5-fancy to non-nil. Or use an ‘OPTIONS’ line in the file to set ‘html5-fancy’.
(setq org-html-doctype "html5") (setq org-html-html5-fancy t)
1.3. Define transcode functions
(defun jdb/html--parse-string (str) "Parse STR and return a DOM." (with-temp-buffer (insert (or str "")) (libxml-parse-html-region (point-min) (point-max)))) (defun jdb/html--build-body (contents info &rest other) "Return information for the <body>..</body> of the HTML output. CONTENTS is the transcoded contents string. INFO is a plist used as a communication channel. OTHER is a plist for additional nodes." (let ((node-body (dom-node "body" ())) (node-layout (dom-node "div" '((id . "layout") (class . "layout")))) (node-main (dom-node "main" '((id . "content") (class . "content")))) (node-toc (dom-node "div" '((id . "toc") (class . "toc"))))) (when (plist-get other :before) (dolist (node (plist-get other :before)) (dom-append-child node-layout node))) (dolist (node (dom-non-text-children (dom-by-tag (jdb/html--parse-string contents) 'body))) (cond ((string= (dom-tag node) "nav") (dom-append-child node-toc node)) (t (dom-append-child node-main node)))) (when (plist-get other :after) (dolist (node (plist-get other :after)) (dom-append-child node-layout node))) (dom-append-child node-layout node-main) (dom-append-child node-layout node-toc) (dom-append-child node-body node-layout))) (defun jdb/html--build-head (info &rest other) "Return information for the <head>..</head> of the HTML output. INFO is a plist used as a communication channel. OTHER is a plist for additional nodes." (setq node-head (dom-node "head" ())) (when (plist-get other :before) (dolist (node (plist-get other :before)) (dom-append-child node-head node))) (dolist (node (dom-children (dom-by-tag (jdb/html--parse-string (plist-get info :html-head)) 'head))) (dom-append-child node-head node)) (dolist (node (dom-children (dom-by-tag (jdb/html--parse-string (plist-get info :html-head-extra)) 'head))) (dom-append-child node-head node)) (when (and (plist-get info :html-htmlized-css-url) (eq org-html-htmlize-output-type 'css)) (dom-append-child node-head (dom-node "link" `((rel . "stylesheet") (href . ,(plist-get info :html-htmlized-css-url)) (type . "text/css"))))) (when (plist-get other :after) (dolist (node (plist-get other :after)) (dom-append-child node-head node))) node-head) (defun jdb/html-template (contents info) "Return complete document string after HTML conversion. CONTENTS is the transcoded contents string. INFO is a plist holding export options." (let* ((title (org-export-data (plist-get info :title) info)) (node-title (dom-node "meta" `((title . ,title)))) (node-charset (dom-node "meta" '((charset . "utf-8")))) (node-author (dom-node "meta" `((author . ,(org-export-data (plist-get info :author) info))))) (node-generator (dom-node "meta" '((generator . "Org")))) (node-head (jdb/html--build-head info :after (list node-charset node-title node-author node-generator))) (node-h1 (dom-node "h1" '((class . "title")) title)) (node-header (dom-node "header" () node-h1)) (node-body (jdb/html--build-body contents info :before (list node-header))) (node-html (dom-node "html" () node-head node-body))) (with-temp-buffer (insert "<!DOCTYPE html>") (dom-print node-html) (buffer-string)))) (org-export-define-derived-backend 'jdb/html 'html :translate-alist '((template . jdb/html-template)))
1.4. Define export functions
(defun jdb/export-subtree-to-html () (interactive) (let ((filename (cdr (assoc "EXPORT_FILE_NAME" (org-entry-properties))))) (org-export-to-file 'jdb/html filename nil t) (shell-command (format "prettier -w --print-width 999 %s" filename)))) (defun jdb/export-to-html () (interactive) (org-babel-tangle) (org-publish "blog") (org-map-entries #'jdb/export-subtree-to-html "EXPORT_FILE_NAME={.+}"))
1.5. Define cleaner postamble
(defun jdb/org-html-postamble (plist) (format "Last update: %s" (format-time-string "%d %b %Y"))) (setq org-html-postamble 'jdb/org-html-postamble)
2. Add a structure
C-c C-,
3. Tangle
C-c C-v t
4. Ensure pretty output
(add-hook 'org-babel-post-tangle-hook #'whitespace-cleanup)
5. Format buffer
C-x h C-M-\ C-u C-SPC C-u C-SPC
6. Add babel execute for Org src
Have Org src within the Org file.
(defun org-babel-execute:org (body params) "Execute a block of Org code with org-babel. BODY is the src code body. PARAMS isn't used." body)