Voglio bloggare con org-mode. So che si può fare facilmente con Hugo, ma a me piacciono Rust e Zola. Questa è la mia casa, non voglio trascolare, ma non voglio nemmeno rinunciare alle meraviglie di org-mode. Ho pensato, perciò, di adattare l'exporter di Hugo (ox-hugo) a Zola, visto che per lo più funziona già di suo e le differenze sono relativamente di minore importanza.

La prima visibile differenza è quella sulle tassonomie. ox-hugo, di suo, non riesce ad esportare delle tassonomie che Zola possa riconoscere. Correggiamo questo difetto.

Taxonomies§

In pratica, hugo spalma tutto nei metadati principali, mentre Zola pretende la sottocategorie "taxonomies". Così in Hugo:

...

tags= ["..."]
categories = ["..."]

Così in Zola:

...

[taxonomies]
    tags = ["..."]
    categories = ["..."]

Questa apparente piccola modifica si poteva ottenere in più modi. Il mio primo pensiero è stato quello di scrivere uno script che modificasse il file finale con delle regex, spostando tags e categories nella parte inferiore del frontmatter ed aggiungendo la stringa "[taxonomies]" subito sopra. Avrebbe fatto il proprio sporco lavoro, ma mi sono detto: e se poi volessi fare altre piccole modifiche? Sarebbe meglio agire direttamente sul sorgente di ox-hugo. Quindi mi sono messo a spulciare le oltre cinquemila linee di codice ed ho trovato delle componenti che mi sembravano alla mia portata.

Ho quindi forkato l'intera repo di ox-hugo (e l'ho chiamato ox-zola)

Come lavora ox-hugo? Innanzitutto crea una alist chiamata prevedibilmente "data", nella quale vengono conservati i metadati del frontmatter. Non bisogna interferire con l'elaborazione di tag e categorie, ma solo manipolare l'association list. Dopodiché, una libreria in Emacs Lisp per lavorare con TOML, tomelr (via `tomelr-encode`) procede a convertire data nella stringa da impiegare come frontespizio in markdown. Per impedire l'apparizione di tags e categorie nell'area principale, basta commentare via le linee corrispondenti:

(data `(;; The order of the elements below will be the order in which the front-matter
                 ;; variables will be ordered.
                 (title . ,(org-hugo--get-sanitized-title info))
                 ...
                 (slug . ,(plist-get info :hugo-slug))
                 ;; (tags . ,tags) <= Comment out line 4177
                 ;; (categories . ,categories) <= Line 4178
                 (type . ,(plist-get info :hugo-type))
                 ...
                 (blackfriday . ,blackfriday)))

A questo punto, aggiungiamo la nostra nuova voce:

(data `(;; The order of the elements below will be the order in which the front-matter
        (title . ,(org-hugo--get-sanitized-title info))
        ...
        (taxonomies . ,taxonomies)
        (blackfriday . ,blackfriday)))

Adesso possiamo sostituire alla voce `taxonomies` la nostra piccola alist da innestare all'interno della principale. Alla linea 4251 troviamo il blocco di codice che si occupava di questo con tags e categories. Commmentiamo via anche queste due righe e sostituiamo opportunamente:

;; Overwrite the 'tags and 'categories key values in `data' with
      ;; the updated values.
      ;; https://stackoverflow.com/a/40815365/1219634
      ;; (setf (alist-get 'tags data) tags)
      ;; (setf (alist-get 'categories data) categories)
      (setq taxonomies `(;; The order of elements below will be respected
                         (tags . tags)
                         (categories . categories)))
      (setf (alist-get 'taxonomies data) taxonomies)

Sembrava tutto pronto, ma purtroppo in questo modo ox-hugo tiene in memoria i tag e le categorie esportate la prima volta. Cioè, se io esporto prima un post che ha dei tag e delle categorie specifiche e poi vado ad esportare un altro post in un altro file org-mode, le tassonomie esportate saranno identiche alle precedenti (a patto che siano trovate delle tassonomie in primo luogo e che siano superati gli "if" che lasciano inserire il tag ad ox-hugo più avanti). Questo bug va sicuramente risolto rinnovando le variabili da qualche parte, ma nel dubbio per il momento ho preferito rimediare con una soluzione più hackerosa e immediata (perché non ho tempo di studiare meglio il codice).

Ho ripristinato le linee di codice commentate sopra ed ho semplicemente aggiunto in una delle funzioni conclusive per il frontmatter (poco prima della conversione in TOML) queste due semplici funzioni:

(setq taxonomies `(;; The order of elements below will be respected
                         (tags . ,(alist-get 'tags data))
                         (categories . ,(alist-get 'categories data))))

      ;; Replace taxonomies in data with actual taxonomies
      (setf (alist-get 'taxonomies data) taxonomies)

Come efficace esempio del risultato di questa operazione, prendiamo il markdown esportato da questo stesso testo che state leggendo:

title = "ox-zola: bloggare con Emacs Org-mode e Zola"
author = ["Giovanni Crisalfi"]
date = 2022-07-30T05:35:00+02:00
tags = ["lisp"]
categories = ["coding"]
draft = false
[taxonomies]
  tags = ["lisp"]
  categories = ["coding"]

Come potete osservare, questa soluzione comporta una ridondanza nel codice esportato in Markdown, ma davvero non c'è problema, anzi, in questo modo si mantiene una duplice compatibilità sia con Hugo che con Zola senza che una o l'altra parta del frontmatter possano interferire.

Per il momento lo terrò così, in futuro, appena avrò tempo, tornerò a lavorarci per raffinare il lavoro.

Il codice completo si trova qui.