deschneme.lua

April 13th, 2017 by Felix Schneider

Yet another static site generator is out there. This time combining lua, jinja2 and JSON.

Einführung

Blogs sind in der heutigen Zeit allgegenwärtig. Es ist heutzutage nicht mehr schwer, einen solchen aufzusetzen. Auch die Bedienung ist nun für Laien möglich. Und das gilt nicht nur für PHP-basierte Systeme, sondern auch für Static Site Generators. Diese bieten gegenüber klassichen dynamischen Platformen mehrere Vorteile:

Prinzipiell erzeugt ein solcher Generator aus einer Menge an Blogposts und Templates vollwertige, fertige HTML-Dateien, welche dann ausgeliefert werden können.
Entscheidet man sich für einen Static Site Generator, hat man viel Auswahl. Im Internet widmen sich mehrere Seiten der Indizierung dieser Menge. Dies ist wohl kein Zufall, denn allzu viel steckt nicht dahinter. Im hier vorgestellten Fall mögen es wohl rund 8 Mannstunden sein.
Weithin Bekanntheit hat wohl Jekyll erreicht. Das Tool wurde in Ruby geschrieben und wird nun (angeblich) bei GitHub Pages eingesetzt. Es kann Markdown, aber auch Textile verarbeiten und verwendet liquid als Template-Engine. Doch mit innerer Einfachheit konnte es nicht überzeugen. So kam es eines Nachmittags, dass das Programm wieder einmal nicht funktionierte. Es war lokal neben dem Vorlageordners dieses Blogs installiert und eingerichtet und hatte in ebendieser Position schon funktioniert. Dem Fehler nachgehen lag nicht in der Luft und so kam es, dass die Idee zu einem einfacheren, lokal leicht installierbaren, transparenten Static Site Generator geboren wurde.

Technologie

Die Wahl der Technologie wurde nach etwas Recherche klar:

Philosophie

Das Programm baut eine Website grundsätzlich in folgenden Schritten auf:

  1. Im ersten Durchgang werden die konfigurierten Variablen indexiert.
  2. Im zweiten Durchgang werden Dateien kopiert oder geparst.

Es verbirgt sich keine große Magie dahinter. Es ist, sozusagen, dumm. Es wertet nicht, und es wertet kaum Felder inhaltlich aus. Es tut nur, was man ihm sagt zu tun, nicht mehr und nicht weniger (sofern es nicht ein Bug ist). Sämtliche Intelligenz verbirgt sich im Layout, oder in der Struktur der Website.

Verwendung

Aufruf

Ganz einfach: lua deschneme.lua <in_dir> [out_dir]
Dabei ist in_dir das Verzeichnis, welches als Quelle für die weiteren Schritte dienen soll. out_dir hingegen muss nicht spezifiziert sein, es kann auch in der Konfigurationsdatei im in_dir geschrieben stehen.

Struktur des Quellverzeichnisses

In dem Quellverzeichnis muss es mindestens folgende Dateien und Ordner geben: (Pfade sind relativ zum Quellverzeichnis)

Im site/-Unterverzeichnis werden zudem folgende Dateien geparst:

Zu den Dateien

Im folgenden wird die Verwendung der verschiedenen Dateien erläutert.

config.json

Diese Datei stellt die Hauptkonfiguration der Seite dar. In ihr können Einstellungen getätigt werden, sowie Vorgabewerte definiert werden. Eine Beispielkonfiguration könnte wie folgt aussehen:

{
    "dest_dir": "output/",
    "update": false,
    "indizes": [ "type" ],
    "default": {
        "layout": "post.html",
        "type": "site",
        "title": "Lorem Ipsum",
        "author": "Me and only me.",
        "lang": "de",
        "public": ["layout", "title"]
    }
}

Erklärung der Felder:

site/*/_config.json

Existiert eine Datei mit diesem Namen in einem Unterverzeichnis der Seite, wird sie geparst und die Werte in die Umgebung der im Ordner oder hierachisch darunter enthaltenen Dateien hinzugefügt. Ein Beispiel möge sein:

{
    "type": "post",
    "author": "Not me now.",
    "lang": "pt-br",
    "public": ["type", "author"]
}

site/*.json

Die Datei endet auf JSON, heißt jedoch nicht _config.json. Sie wird geparst und prinzipiell wie eine Markdown-Datei ohne Markdown behandelt. Damit ist sie beispielsweise für zweisprachige Seiten oder zum Definieren von kurzen Inhalten in mehreren Bereichen einer Seite in vielen Feldern geeignet. Ein Beispiel:

{
    "title": "Near, far, yonder.",
    "author": "Not me. But neither was he.",
    "quotes": [
        "Not tomorrow nor today.",
        "Near, far, yonder.",
        "Come a little closer ;)"
    ],
    "footer": "This is special today! Make it count ;)"
}

Beim Übersetzen wird das im JSON genannte Template angewandt, und die ursprüngliche Endung durch die des Templates ersetzt.

site/*.md & site/*.markdown

Die Datei beinhaltet Markdown, sowie ein JSON Frontmatter. Dieses wird durch eine Leerzeile (also einen doppelten Zeilenumbruch, \n\n) vom Markdown getrennt. Beim Übersetzen wird auf die Datei das im Frontmatter genannte Template angewandt und das Ergebnis mit Namen der Datei und Endung des Templates ausgestattet. Der Inhalt des Markdowns ist über loctbl.content zugänglich.

site/*.jinja2

Auch eine Datei wie diese muss ein Frontmatter haben. Allerdings lässt sich in diesem Fall weniger das Verhalten der Datei bestimmen als sonst. Im Gegensatz zu den anderen Dateien wird beim Parsen nicht auf die layout-Variable rücksicht genommen. Die Ausgabedatei heißt wie die Alte, nur ohne der .jinja2-Endung.

Felder im JSON

Generell sind die JSON-Felder zur freien Gestaltung bestimmt. Lediglich ein paar werden von dem Programm direkt verwendet, der Rest wird, so wie er ist, an das Template übergeben.

Folgende Felder werden von deschneme interpretiert:

{
    "layout": "path/to/layout.html",
    "public": ["author", "bla"],
    /* elements defined in "indizes", eg: */
    "type": [ "post", "site" ]
}

Diese müssen, wie immer, nicht direkt im Frontmatter/JSON spezifiziert werden, sondern können auch von vorhergehenden Konfigurationsdateien gesetzt worden sein. Es greift also die Vererbung.

Erklärung der Felder:

Variablen in der Template-Umgebung

Folgende Variablen stehen in den Templates zur Verfügung:

Funktionen in der Template-Umgebung

Folgende Funktionen stehen zur Verfügung:

paginate_setup (data, el_p_run, name_root, cur_path):
Aktiviere Pagination. Die Funktion gibt eine Tabelle mit folgenden Einträgen zurück: cur_page, page_amount, prev_path, next_path sowie data. Die Parameter bedeuten:
data: ein Array mit zu spaltenden Quelldaten
el_p_run: Anzahl an Elementen, die pro Seite gelistet werden sollen
name_root: Stamm-Name der einzelnen Seiten-Dateien, ohne Endung. An sie wird dann eine entsprechende Zahl angehängt.
cur_path: Pfad der aktuellen Datei. Wird für Aufbau des nächsten Links verwendet.

Erweiterungsmechanismus:

Um die eingebauten Funktionen erweitern zu können, werden Dateien mit der Endung .lua in folgenden Verzeichnissen in beschriebener Reihenfolge eingelesen:

  1. vom Unterverzeichnis ./ext/ des Programmordners, falls nicht anders konfiguriert.
  2. vom Unterverzeichnis extensions/ des Seiten-Ordners

Sie werden über dofile() geladen und sollen eine Tabelle zurückgeben. Diese darf folgende Indizes haben:

Tipps und Tricks

deschneme.lua baut im Update-Mode nur Dateien neu auf, die einen neueren Modification-Timestamp als ihre Pendants im Build-Verzeichnis haben. Somit fallen Listen immer unter den Tisch. Um dem zu helfen, empfiehlt sich, diese vor jedem Build zu touchen oder im Build-Verzeichnis zu entfernen. Es ist nicht Aufgabe von deschneme.lua, dies zu tun. Es wäre sogar fast ein Verbrechen, wenn das Programm in dem Quellverzeichnis herumpfuscht.

Selbst verwenden

Der Sourcecode kann hier heruntergeladen werden: deschneme-v1.2.tar.bz2 Er ist als Public Domain freigegeben, auf dass er möglichst nützlich sein mag.

Zur Verwendung ist allerdings noch mehr als nur diese Quelle nötig:

Die Module können installiert werden, oder aber sie werden einfach in das Unterverzeichnis ./lib/ gelegt. Dort sucht deschneme auch danach. Aber Achtung: der Pfad bezieht sich auf das Arbeitsverzeichnis beim Aufruf, es empfiehlt sich daher folgende Form: `lua deschneme.lua ../path/to/site/root`.

TL;DR

Es handelt sich um einen Static Site Generator, der durch Universalität und Einfachheit zu glänzen versucht. Geschrieben ist er in lua, dazu verwendet er jinja2-Templates, JSON zur Konfiguration, sowie Markdown für längere Inhalte. Das Werkzeug versucht, einfach und unkompliziert in der Handhabung zu sein, Installation mit eingeschlossen.