|
|
#+TITLE: Ox-Hugo: A carefully crafted Org exporter back-end that enables writing Hugo posts in Org |
|
|
#+AUTHOR: Kaushal Modi |
|
|
[[https://travis-ci.org/kaushalmodi/ox-hugo][https://travis-ci.org/kaushalmodi/ox-hugo.svg?branch=master]] [[https://melpa.org/#/ox-hugo][file:https://melpa.org/packages/ox-hugo-badge.svg]] [[https://www.gnu.org/licenses/gpl-3.0][https://img.shields.io/badge/License-GPL%20v3-blue.svg]] |
|
|
=ox-hugo= is an Org exporter backend that exports Org to |
|
|
[[https://gohugo.io/][Hugo]]-compatible Markdown ([[https://github.com/russross/blackfriday][Blackfriday]]) and also generates the |
|
|
front-matter (in TOML or YAML format). |
|
|
|
|
|
This project consists of =ox-blackfriday.el= too. It is a derivation |
|
|
of [[https://github.com/larstvei/ox-gfm][=ox-gfm=]] with support added for Blackfriday Markdown tables and |
|
|
many other tweaks. =ox-hugo= backend extends from this. |
|
|
|
|
|
There are 2 major blogging flows that can be used with this package: |
|
|
1. One post per Org subtree (preferred) |
|
|
- Export only the *current* post Org subtree, or |
|
|
- Export all valid Hugo post subtrees in a loop. |
|
|
2. One post per Org file |
|
|
- This works but you won't be able to leverage Org-specific |
|
|
benefits like tag and property inheritance, use of TODO states to |
|
|
translate to post =draft= state, auto weight calculation for |
|
|
posts and menu items, etc. |
|
|
|
|
|
See the [[https://github.com/kaushalmodi/ox-hugo/wiki/Org-Capture-Setup][Org Capture Setup]] Wiki page to see how to quickly create new |
|
|
posts. |
|
|
|
|
|
See the [[https://github.com/kaushalmodi/ox-hugo/wiki/Auto-export-on-Saving][Auto-export on Saving]] Wiki page to learn how to setup up |
|
|
seeing live-preview of the Hugo-rendered HTML each time you do =C-x |
|
|
C-s= in the Org file --- For now, this works only with the *Subtree |
|
|
export flow*. |
|
|
* Demo |
|
|
#+BEGIN_CENTER |
|
|
[[https://github.com/kaushalmodi/ox-hugo/tree/master/test/example-site/content-org][Org source]] → [[https://github.com/kaushalmodi/ox-hugo/tree/master/test/example-site/content][Exported Markdown]] → [[https://ox-hugo.netlify.com][Hugo published site -- https://ox-hugo.netlify.com]] |
|
|
#+END_CENTER |
|
|
* Table of Contents |
|
|
- [[#demo][Demo]] |
|
|
- [[#screenshots][Screenshots]] |
|
|
- [[#why-ox-hugo][Why =ox-hugo=?]] |
|
|
- [[#installation][Installation]] |
|
|
- [[#usage][Usage]] |
|
|
- [[#before-you-export][Before you export]] |
|
|
- [[#export-bindings][Export bindings]] |
|
|
- [[#example-hugo-site-to-test-this-package][Example Hugo site to test this package]] |
|
|
- [[#how-to-try-ox-hugo-on-that-site][How to try =ox-hugo= on that site?]] |
|
|
- [[#alternative-way][Alternative way]] |
|
|
- [[#how-do-i-try-ox-hugo-on-my-site][How do I try =ox-hugo= on my site?]] |
|
|
- [[#translation-of-org-meta-data-to-hugo-front-matter][Translation of Org meta-data to Hugo front-matter]] |
|
|
- [[#for-subtree-exports-c-c-c-e-h-h-or-c-c-c-e-h-a][For subtree exports (=C-c C-e H H= or =C-c C-e H A=)]] |
|
|
- [[#for-complete-file-exports-c-c-c-e-h-h][For complete-file exports (=C-c C-e H h=)]] |
|
|
- [[#formatting][Formatting]] |
|
|
- [[#do-i-need-to-re-write-my-whole-blog-in-org][Do I need to re-write my whole blog in Org?]] |
|
|
- [[#changelog][Changelog]] |
|
|
- [[#debug][Debug]] |
|
|
- [[#test][Test]] |
|
|
- [[#thanks][Thanks]] |
|
|
|
|
|
* Screenshots |
|
|
Before you read further, you can see below how =ox-hugo= translates |
|
|
Org to Markdown (Org on the left; exported Markdown with Hugo |
|
|
front-matter on the right). |
|
|
*One post per Org subtree --* |
|
|
[[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/doc/images/one-post-per-subtree.png][https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/doc/images/one-post-per-subtree.png]] |
|
|
- Files in above screenshot :: [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/example-site/content-org/screenshot-subtree-export-example.org][Org]] -> [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/example-site/content/writing-hugo-blog-in-org-subtree-export.md][Markdown]] |
|
|
*One post per Org file --* |
|
|
[[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/doc/images/one-post-per-file.png][https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/doc/images/one-post-per-file.png]] |
|
|
- Files in above screenshot :: [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/example-site/content-org/writing-hugo-blog-in-org-file-export.org][Org]] -> [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/example-site/content/writing-hugo-blog-in-org-file-export.md][Markdown]] |
|
|
|
|
|
The preferred way to organize the posts is as Org subtrees (also the |
|
|
main reason to write this package, as nothing like that was out there) |
|
|
as it makes the meta-data management for Hugo front-matter pretty |
|
|
effortless. |
|
|
|
|
|
If you are a /one Org-file per post/ type of a person, that flow works |
|
|
too! Just note that in this flow many of those =#+HUGO_= properties |
|
|
need to be managed manually.. just as one would manage the front-matter |
|
|
in Markdown files --- See the Org versions in the above screenshots for |
|
|
comparison. |
|
|
* Why =ox-hugo=? |
|
|
Using Org just as a markup like Markdown is a miniscule part of its |
|
|
complete feature-set. Org also allows stuff like: |
|
|
- Easy ordering/manipulation/commenting of subtrees |
|
|
- Creating tables (with even formulas like in Excel) |
|
|
- Directly including source code snippets from external files (instead |
|
|
of having to copy/paste them in) |
|
|
- Running code snippets within the Org file and embedding the results |
|
|
(Org Babel) |
|
|
- .. |
|
|
|
|
|
Using Org for content writing allows using in-built Org features to |
|
|
translate to Hugo front-matter: |
|
|
|
|
|
- Org uses an outline structure and can inherit meta data (tags and |
|
|
properties) from one subtree to children subtrees. |
|
|
- Using that feature, one can tag one tree as /emacs/, and everything |
|
|
under that tree (all posts under that) will get that tag |
|
|
automatically. |
|
|
- The same concept applies to inheriting any Org /property/ meta data |
|
|
like menu entry, category, section name, etc. |
|
|
- A subtree can be quickly marked to be in TODO state (default binding =C-c C-t=). A *TODO* post is marked as a /draft/ Hugo post. |
|
|
- The /menu-item weights/ and/or /post weights/ can be set to be |
|
|
auto-calculated so that the menu items or post order in the final |
|
|
HTML appear in the same order as the respective subtrees in Org. |
|
|
|
|
|
If the subtrees are re-ordered in Org, the weights get changed too. |
|
|
- One can have a subtree with section property set to "posts" and all |
|
|
post subtrees under that will go to that section. Similarly another |
|
|
parent subtree can have that property set to "articles", and so on. |
|
|
- Images can be displayed inline in the Org buffer. |
|
|
- After save hooks can be set up in Emacs so that each time I save the |
|
|
file, only the current subtree in Org gets exported to |
|
|
Markdown. With the Hugo server running with the new switch that auto |
|
|
changes the preview to the last changed post (=--navigateToChanged= |
|
|
introduced in Hugo 0.25), the flow is seamless -- Save the Org file |
|
|
and see the exact changed post in browser. |
|
|
- *All* posts can simply be subtrees in a single Org file. That way |
|
|
one can take advantage of Org subtree filtering and searching |
|
|
functions (=org-sparse-tree= bound to =C-c /= by default). |
|
|
- (and much more..) |
|
|
* Installation |
|
|
This package requires emacs 24.5+ and Org 9.0+. It is available on Melpa. |
|
|
* Usage |
|
|
Once the package is installed, you will need to require it so that the |
|
|
=ox-hugo= export options are available in the /Org Export Dispatcher/ |
|
|
menu (the one you see when you hit =C-c C-e= to initiate any export). |
|
|
|
|
|
You can do that by adding the below to your config: |
|
|
#+BEGIN_SRC emacs-lisp |
|
|
(with-eval-after-load 'ox |
|
|
(require 'ox-hugo)) |
|
|
#+END_SRC |
|
|
If you use =use-package=, you can do the below instead: |
|
|
#+BEGIN_SRC emacs-lisp |
|
|
(use-package ox-hugo |
|
|
:after ox) |
|
|
#+END_SRC |
|
|
** Before you export |
|
|
Before you export check that these properties are set as you need: |
|
|
- HUGO_SECTION :: The default Hugo section name for all the posts. See |
|
|
[[http://gohugo.io/content/sections/][here]] for more information on Hugo sections. It is |
|
|
common for this property to be set to =posts= or =blog=. The default value is set using =org-hugo-default-section-directory=. |
|
|
- HUGO_BASE_DIR :: Root directory of the source for the Hugo site. If |
|
|
this is set to =~/hugo/=, the exported Markdown |
|
|
files will be saved to =~/hugo/content/<HUGO_SECTION>/= directory. By |
|
|
default, the Markdown files reside in a hierarchy |
|
|
under the =content/= directory in the site root |
|
|
directory ([[http://gohugo.io/content/organization/][ref]]). If you try to export without |
|
|
setting this property, you will get this error: |
|
|
#+BEGIN_EXAMPLE |
|
|
user-error: It is mandatory to set the HUGO_BASE_DIR property |
|
|
#+END_EXAMPLE |
|
|
*Important*: If you choose to export an Org subtree as a post, you |
|
|
need to set the =EXPORT_FILE_NAME= subtree property. That property is |
|
|
used by this package to figure out where the current post starts. |
|
|
** Export bindings |
|
|
The common =ox-hugo= export bindings are: |
|
|
|---------------+-------------------------------------------------------------------------------------| |
|
|
| Binding | Description | |
|
|
|---------------+-------------------------------------------------------------------------------------| |
|
|
| =C-c C-e H H= | Export only the current /valid/ subtree (has the =EXPORT_FILE_NAME= property set) | |
|
|
| =C-c C-e H A= | Export *all* /valid/ subtrees (those that have the =EXPORT_FILE_NAME= property set) | |
|
|
|---------------+-------------------------------------------------------------------------------------| |
|
|
| =C-c C-e H h= | Export the whole Org file to a single post | |
|
|
|---------------+-------------------------------------------------------------------------------------| |
|
|
* Example Hugo site to test this package |
|
|
An [[https://github.com/kaushalmodi/ox-hugo/tree/master/test/example-site][example-site]] with bare-bones "theme" is used to live-test the |
|
|
package --- you'll know why theme is double-quoted once you try out the |
|
|
example-site on =hugo=. |
|
|
|
|
|
Check out the [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/example-site/content-org/all-posts.org][example single Org file]]. That is created for testing various |
|
|
Org->Hugo content and meta-data translation features. [[https://github.com/kaushalmodi/ox-hugo/tree/master/test/example-site/content/posts][Here]] are the |
|
|
exported Markdown files. |
|
|
** How to try =ox-hugo= on that site? |
|
|
1. Clone this repo. |
|
|
2. =cd= to the =test/example-site/= directory and do: |
|
|
#+BEGIN_EXAMPLE |
|
|
make serve |
|
|
#+END_EXAMPLE |
|
|
- *Requires Hugo 0.25+* |
|
|
3. Open =http://localhost:1337= in your browser. |
|
|
4. In a different terminal, =cd= to the same =test/example-site/= directory. |
|
|
5. Run: |
|
|
#+BEGIN_EXAMPLE |
|
|
make mdtree ORG=content-org/all-posts.org |
|
|
#+END_EXAMPLE |
|
|
6. In few seconds, dozens of test posts will get created, with the =hugo server= aided preview in the browser zapping through each new |
|
|
created post. |
|
|
** Alternative way |
|
|
1. Clone this repo. |
|
|
2. =cd= to the =test/example-site/= directory and do: |
|
|
#+BEGIN_EXAMPLE |
|
|
hugo server -D --navigateToChanged |
|
|
#+END_EXAMPLE |
|
|
- =--navigateToChanged= requires Hugo 0.25+. |
|
|
3. Above command will mention the localhost where the site is |
|
|
served. Open that in your browser. |
|
|
4. In emacs, =(require 'ox-hugo)= or evaluate the =ox-hugo.el= from the |
|
|
cloned repo. |
|
|
5. Open the [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/example-site/content-org/all-posts.org][=all-posts.org=]] file. |
|
|
6. =C-c C-e H A= -- That will export *all* subtrees in the file to |
|
|
Markdown files. |
|
|
7. In few seconds, dozens of test posts will get created, with the =hugo server= aided preview in the browser zapping through each new |
|
|
created post (needs that new feature =--navigateToChanged= |
|
|
introduced in Hugo 0.25). |
|
|
* How do I try =ox-hugo= on my site? |
|
|
1. =cd= to your Hugo site base directory -- the one that contains the =config.toml= (or =config.yaml= or =config.json=). |
|
|
2. Start the =hugo server= in that directory: |
|
|
#+BEGIN_EXAMPLE |
|
|
hugo server -D --navigateToChanged |
|
|
#+END_EXAMPLE |
|
|
- =--navigateToChanged= requires Hugo 0.25+. |
|
|
3. Above command will mention the localhost where the site is |
|
|
served. Open that in your browser. |
|
|
4. Create a separate directory for Org content in the Hugo site base |
|
|
directory. You can name it anything, but I prefer to name it =content-org= ([[https://github.com/kaushalmodi/ox-hugo/tree/master/test/example-site][Example 1 -- =ox-hugo= example site]], [[https://gitlab.com/kaushalmodi/kaushalmodi.gitlab.io][Example 2 -- My |
|
|
blog]]). |
|
|
5. Create an Org file in there and follow the *Usage* section in the |
|
|
[[https://github.com/kaushalmodi/ox-hugo#usage][README]] or [[https://github.com/kaushalmodi/ox-hugo/wiki/Usage][Wiki]] to export it. |
|
|
* Translation of Org meta-data to Hugo front-matter |
|
|
** For subtree exports (=C-c C-e H H= or =C-c C-e H A=) |
|
|
When organizing the posts as Org *subtrees*, many Hugo front-matter |
|
|
variables get set implicitly using the meta-data parsed from the posts |
|
|
in Org. |
|
|
|
|
|
Below, where /subtree/ is mentioned, it implies a *valid Hugo-post |
|
|
subtree* i.e. an Org subtree that has the =EXPORT_FILE_NAME= property |
|
|
set. |
|
|
|------------------------------------+------------------------------------+-------------------------------------------------------------------------| |
|
|
| Hugo front-matter (TOML) | Org | Org description | |
|
|
|------------------------------------+------------------------------------+-------------------------------------------------------------------------| |
|
|
| =title = "foo"= | =* foo= | Subtree heading | |
|
|
| =date = 2017-09-11T14:32:00-04:00= | =CLOSED: [2017-09-11 Mon 14:32]= | Auto-inserted =CLOSED= subtree property when switch to Org *DONE* state | |
|
|
| =date = 2017-07-24= | =:EXPORT_DATE: 2017-07-24= | Subtree property | |
|
|
| =lastmod = <current date>= | =:EXPORT_HUGO_AUTO_SET_LASTMOD: t= | Subtree property | |
|
|
| =lastmod = <current date>= | =#+HUGO_AUTO_SET_LASTMOD: t= | Org keyword | |
|
|
| =tags = ["abc", "def"]= | =* foo :abc:def:= | Subtree heading tags | |
|
|
| =categories = ["x", "y"]= | =* foo :@x:@y:= | Subtree heading tags with =@= prefix | |
|
|
| =draft = true= | =* TODO foo= | Subtree heading Org Todo state set to =TODO= (or =DRAFT=) | |
|
|
| =draft = false= | =* foo= | Subtree heading Org Todo state *not* set to =TODO= (or =DRAFT=) | |
|
|
| =weight = 123= | =:EXPORT_HUGO_WEIGHT: auto= | When set to =auto=, weight is auto-calculated | |
|
|
| =weight = 123= (in =[menu.foo]=) | =:EXPORT_HUGO_MENU: :menu foo= | Menu weight is auto-calculated unless specified | |
|
|
|------------------------------------+------------------------------------+-------------------------------------------------------------------------| |
|
|
*** Notes |
|
|
- Precedence for =date= parsing: =CLOSED= subtree property /more than/ =EXPORT_DATE= subtree property /more than/ =#+DATE:= keyword. |
|
|
** For complete-file exports (=C-c C-e H h=) |
|
|
|----------------------------------+--------------------------------------| |
|
|
| Hugo front-matter (TOML) | Org | |
|
|
|----------------------------------+--------------------------------------| |
|
|
| =title = "foo"= | =#+TITLE: foo= | |
|
|
| =date = 2017-07-24= | =#+DATE: 2017-07-24= | |
|
|
| =lastmod = <current date>= | =#+HUGO_AUTO_SET_LASTMOD: t= | |
|
|
| =tags = ["abc", "def"]= | =#+HUGO_TAGS: abc def= | |
|
|
| =categories = ["x", "y"]= | =#+HUGO_CATEGORIES: x y= | |
|
|
| =draft = true= | =#+HUGO_DRAFT: true= | |
|
|
| =draft = false= | =#+HUGO_DRAFT: false= (default) | |
|
|
| =weight = 123= | =#+HUGO_WEIGHT: 123= | |
|
|
| =weight = 123= (in =[menu.foo]=) | =#+HUGO_MENU: :menu foo :weight 123= | |
|
|
|----------------------------------+--------------------------------------| |
|
|
*** Notes |
|
|
- The auto weight calculation for posts and menu items works *only* |
|
|
for subtree exports. For the complete-file export flow, one needs to |
|
|
specify the weights manually if needed. |
|
|
* Formatting |
|
|
Below table shows the translation of Org markup to Markdown markup in |
|
|
the exported =.md= files. |
|
|
|
|
|
See the Org source in [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/example-site/content-org/all-posts.org][=all-posts.org=]] under /Formatting/ -> /General/ |
|
|
heading and how it exports to Markdown in [[https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/example-site/content/posts/general-formatting.md][=general-formatting.md=]]. |
|
|
|--------------------+--------------------------------------------------------------------| |
|
|
| Org | Markdown | |
|
|
|--------------------+--------------------------------------------------------------------| |
|
|
| =*bold*= | =**bold**= | |
|
|
| =/italics/= | =_italics_= | |
|
|
| ==monospace== | =`monospace`= | |
|
|
| =~key-binding~= | =<kbd>key-binding</kbd>= | |
|
|
| | - if =org-hugo-use-code-for-kbd= is non-nil [default] | |
|
|
| | - Requires *CSS* to render the =<kbd>= tag as something special. | |
|
|
| =~key-binding~= | =`key-binding`= | |
|
|
| | - if =org-hugo-use-code-for-kbd= is nil | |
|
|
| =+strike-through+= | =~~strike-through~~= | |
|
|
| =_underline_= | =<span class = "underline">underline</span>= | |
|
|
| | - Requires *CSS* to render this =underline= class as an underline. | |
|
|
|--------------------+--------------------------------------------------------------------| |
|
|
|
|
|
(Note: If you see two equal signs on each side of /monospace/ in the |
|
|
/Org/ column in the table above, it is a bug with GitHub's Org |
|
|
renderer.. just see those as *single* equal signs on each side of |
|
|
/monospace/ instead.) |
|
|
* Do I need to re-write my whole blog in Org? |
|
|
If you are considering to try out =ox-hugo=, and if you have already |
|
|
been using Hugo, it is normal for this thought to cross your mind: |
|
|
#+BEGIN_QUOTE |
|
|
I already have dozens or hundreds of posts written in Markdown. Do I |
|
|
need to convert them to Org if I want to start using =ox-hugo=? |
|
|
#+END_QUOTE |
|
|
|
|
|
The answer is *No*. |
|
|
|
|
|
This package will export your future posts written in Org to |
|
|
Markdown. And those files will live along with your already written |
|
|
Markdown posts. So converting existing Markdown files to Org would be |
|
|
purely the user's choice, your choice -- but that's by no means a |
|
|
necessity if you want to start using =ox-hugo=. |
|
|
|
|
|
.. And if at some point, you want to stop using =ox-hugo=, you still |
|
|
have the exported Markdown files. |
|
|
* Changelog |
|
|
** 0.1.3 <2017-09-13 Wed> |
|
|
- Now a HUGO key value set to ="nil"=, like =#+HUGO_CODE_FENCE: nil=, |
|
|
will evaluate as /nil/ instead of /t/, as now =org-hugo--plist-get-true-p= is used to parse boolean keys instead |
|
|
of =plist-get=. |
|
|
** 0.1.2 <2017-09-12 Tue> |
|
|
- Make DateTime matching better; new internal variable =org-hugo--date-time-regexp=. Earlier time zones ahead of UTC (with =+= sign) were not detected as dates in =org-hugo--quote-string= and |
|
|
thus were unnecessarily quoted. |
|
|
** 0.1.1 <2017-09-11 Mon> |
|
|
- Use CLOSED log drawer info if available to set the date in |
|
|
front-matter [[[https://github.com/kaushalmodi/ox-hugo/issues/68][68]]]. |
|
|
- Code optimization: Use of =org-entry-get= at places instead of |
|
|
maintaining global variables. |
|
|
* Debug |
|
|
If the =ox-hugo= exports do not work as expected, or if you get an |
|
|
error backtrace, |
|
|
1. Open an [[https://github.com/kaushalmodi/ox-hugo/issues][Issue]]. |
|
|
2. Describe the problem you are seeing. |
|
|
3. Provide the debug info using =org-hugo-debug-info=: |
|
|
- =M-x org-hugo-debug-info= (that will copy the debug info in |
|
|
Markdown format to the kill ring) |
|
|
- Paste the Markdown contents in the GitHub issue. |
|
|
- You can still hit the /Preview/ tab of the Issue before |
|
|
submitting it. |
|
|
* Test |
|
|
1. Clone this repo. |
|
|
2. =cd= to the =test/example-site/= directory and do: |
|
|
#+BEGIN_EXAMPLE |
|
|
make test |
|
|
#+END_EXAMPLE |
|
|
* Thanks |
|
|
- Matt Price (@titaniumbones) |
|
|
- Puneeth Chaganti (@punchagan) |
|
|
- Also thanks to [[http://www.holgerschurig.de/en/emacs-blog-from-org-to-hugo/][holgerschurig.de]], [[http://whyarethingsthewaytheyare.com/setting-up-the-blog/][whyarethingsthewaytheyare.com]] and |
|
|
the [[https://github.com/chaseadamsio/goorgeous][=goorgoeous=]] project by Chase Adams (@chaseadamsio) for |
|
|
inspiration to start this project. |
|
|
|
|
|
|