Tipps zum Schreiben von Antora-Makros
Unser Dokumentationsteam hat vor kurzem auf eine codebasierte Dokumentation umgestellt, die in eine statische Website übertragen wird. Wir haben uns für die Arbeit mit Antora und AsciiDoc entschieden und zwei Makros entwickelt, um die Funktionalität von Antora zu erweitern und unsere Dokumentation noch weiter zu verbessern. In diesem Blog möchten wir unsere Erfahrungen beim Schreiben von Antora-Makros teilen.
Magnolia in Aktion
Unser Expertenteam zeigt Ihnen live, was Magnolia für Sie leisten kann.
Jetzt Demo buchenAuswahl und Migration
Nach einigen Jahren des Nachdenkens über die Umstellung auf ein Docs-as-Code-Konzept hat das Team ein Proof of Concept durchgeführt und die Optionen auf Docusaurus, einen statischen Website-Generator in Markdown, und Antora, das AsciiDoc als Auszeichnungssprache verwendet, eingegrenzt.
Antora und AsciiDoc gewannen aufgrund ihrer nativen Funktionen, wie Include-Direktiven und der Möglichkeit, Inhalte aus mehreren Git-Repositories zu holen. Die Entscheidung wurde auch durch unser Ziel beeinflusst, eng mit unseren Entwicklern zusammenzuarbeiten. Wir wollten die Dokumentation an ihren typischen Arbeitsablauf anpassen, um sie zu ermutigen, zu unseren technischen Inhalten beizutragen.
Um es kurz zu machen: Die Migration umfasste die folgenden Schritte:
Konvertierung des Backend-Inhalts in Markdown und anschließende Konvertierung von Markdown in AsciiDoc mit pandoc
Einrichten der anfänglichen Antora-Struktur für unsere Dokumentations-Site
- die unserer alten Site so ähnlich wie möglich sein sollte
- um den Übergang zu erleichtern
Einrichten von Algolia zum Crawlen und Indizieren unserer Seite für die Suche
Anpassung der Antora-Benutzeroberfläche an unsere Unternehmenspalette
Schreiben von Antora-Makros
Warum ein Makro schreiben?
Obwohl Antora unseren technischen Redakteuren und Entwicklern von Haus aus eine beträchtliche Menge an Funktionen bietet, wollten wir Erweiterungen erstellen, um unsere Dokumentation noch weiter zu verbessern. Die Antora-Makros haben sich als nützlich erwiesen, und wir werden uns hier zwei einfache Beispiele ansehen.
Die Herausforderung bei der Verwendung einer statischen Website ist der dynamische Inhalt. Im Fall von Magnolia haben die Module unterschiedliche Release-Zyklen, und es würde sich nicht lohnen, die einzelnen Modulversionen manuell zu verfolgen. Darüber hinaus wäre dies fehleranfällig. Stattdessen wollten wir diese Informationen automatisch abrufen und dem Benutzer anzeigen.
Ein weiterer Anwendungsfall für Makros ist unsere Java-Dokumentation. Die Dokumentation verweist auf die Java-Dokumentation an verschiedenen Stellen. Die Dokumentation einer Klasse hängt unter anderem vom Modul der Klasse sowie von ihrer Version ab. Die Version ändert sich mit der Zeit, und die Klasse kann verschoben oder umbenannt werden. Wir haben uns entschieden, ein Makro zu erstellen, das diese Informationen automatisch auf dem neuesten Stand hält.
Die obigen Beispiele sind einfach, aber ich musste viel Zeit damit verbringen, zu recherchieren, wie ich anfangen soll. Obwohl ich großartige Hilfe bekam, hätte ich fast aufgegeben, denn dieses Thema ist überhaupt nicht dokumentiert, und deshalb möchte ich es heute erklären! Lasst uns eintauchen.
Auswählen eines Erweiterungstyps
Die Asciidoctor.js Dokumentation listet die folgenden Erweiterungstypen auf:
Block-Makro-Prozessor
Inline-Makro-Prozessor
Block-Prozessor
Include-Prozessor
Präprozessor
Postprozessor
Baumprozessor
Docinfo-Prozessor
Wir werden uns zwei davon ansehen, den Inline-Makro-Prozessor und den Postprozessor. Ich hoffe, dass diese Beispiele Ihnen helfen, eine der Erweiterungen zu implementieren.
Exploring Reusable Web Components
This blog explores the 3 main technologies of WebComponenets: HTML templates, shadow DOMs, and custom elements. To learn more, read the blog and check out our code on Git.
Schreiben eines Inline-Makro-Prozessors
Mein erster Erfolg war, einen Inline-Makro-Prozessor zum Laufen zu bringen, dank eines Beispiels, das ich in einem Fedora-Dokumentations-Pull-Request fand.
Das ist es, was die komplizierte Syntax tut:
Die Package-Erweiterung ist registriert und parst package:asciidoctor wenn sie im Dokument vorkommt.
Die obige Zeile wird in einen Link umgewandelt
- d.h. https://apps. fedoraproject.org/packages/asciidoctor
- was das Überschreiben der Stamm-URL mit der Seiteneigenschaft url-package-url-format auf Dokumentenebene ermöglicht.
Glücklicherweise habe ich festgestellt, dass es jetzt möglich ist, eine einfachere Syntax zu verwenden.
Um herauszufinden, in welchem JAR sich eine Klasse befindet, habe ich das Makro javadoc implementiert, das Magnolias Nexus-Instanz abfragt, wenn eine Klasse wie folgt definiert ist:
javadoc:info.magnolia.templating.functions.TemplatingFunctions
Mit Hilfe der Nexus-Antwort konnte ich den Link zur Java-Dokumentation der Klasse zusammenstellen, was zu dieser Makrostruktur führte:
const request = require('sync-request') function initInlineManMacro ({ file }) { return function () { this.process((parent, target, attrs) => { // ... }) } } function register (registry, context) { registry.inlineMacro('javadoc', initInlineManMacro(context)) } module.exports.register = register
Dieses Skript hängt absichtlich von sync-request ab, was in modernem, asynchronem JavaScript kontraintuitiv erscheinen mag. Aber das liegt daran, dass ich experimentell herausgefunden habe, dass AsciiDoc den Inhalt rendert und nicht zu ihm zurückkehrt. Damit die Antwort bereit ist, wenn das Makro aufgerufen wird, muss die Anfrage also blockiert werden.
target wird zu info.magnolia.templating.functions.TemplatingFunctions ausgewertet.
Lassen Sie uns nun die eigentliche Methode process implementieren.
Aufgrund unserer Sicherheitsanforderungen muss das Makro sicherstellen, dass die Anmeldeinformationen angegeben werden. Werden sie nicht angegeben, geht das Makro davon aus, dass wir uns in einer Entwicklungsumgebung befinden, und erzeugt nur einen Dummy-Link:
const attributes = Opal.hash2(['window'], { window: '_blank' }) if (!process.env.NEXUS_USERNAME || !process.env.NEXUS_PASSWORD) { console.log('Umgebungsvariablen NEXUS_USERNAME und/oder NEXUS_PASSWORD nicht vorhanden, Javadoc-Links nicht nachschlagen.') return this.createInline(parent, 'anchor', shortenedClassName, { type: 'link', target: '#', attributes }) }
Schicken wir nun eine Anfrage an Nexus:
// REST-Aufruf an nexus blockieren var res = request('GET', 'https://nexus.magnolia-cms.com/service/local/lucene/search', { qs: { cn: target }, headers: { Accept: 'application/json', // nexus zwingen, JSON statt XML zu erzeugen Authorization: 'Basic ' + Buffer.from(process.env.NEXUS_USERNAME + ':' + process.env.NEXUS_PASSWORD).toString('base64') } })
Jetzt können wir das Ganze abschließen, indem wir die richtigen Werte aus dem empfangenen Objekt holen und die URL erstellen. Ich erspare Ihnen die Details, aber wenn Sie daran interessiert sind, sehen Sie sich bitte die Dateien am Ende dieses Blogs an.
console.log('Nexus search done for: ' + target) // alle Ergebnisse liefern die gleiche groupId, artifactId und latestRelease // also können wir einfach das erste auswählen const nexusResult = JSON.parse(res.getBody('utf8')).data[0] const groupId = nexusResult.groupId const artifactId = nexusResult.artifactId const version = nexusResult.latestRelease let url = 'https://nexus.magnolia-cms.com' ... url += '.html' return this.createInline(parent, 'anchor', shortenedClassName, { type: 'link', target: url, attributes })
So sieht die Seite aus:
Und das ist der HTML-Code:
Diese Seite listet nur die gebräuchlichsten Funktionen auf. Siehe <a href="https://nexus.magnolia-cms.com/service/local/repositories/magnolia.public.releases/archive/info/magnolia/magnolia-templating/6.2.9/magnolia-templating-6.2.9-javadoc.jar/!/info/magnolia/templating/functions/TemplatingFunctions.html" target="_blank" rel="noopener">TemplatingFunctions</a> für die vollständige Liste.
Wir hätten sogar noch weiter gehen können, indem wir dem Makro zum Beispiel einen Parameter hinzugefügt hätten:
javadoc:info.magnolia.rest.ui.field.JsonMultiFieldDefinition[isEnterprise=true]
Mit diesem Zusatz konnte attrs.isEnterprise im Verarbeitungscode ausgewertet werden. Wenn der Wert positiv war, konnten wir die endgültige URL unter Verwendung des Repositorys magnolia.enterprise.releases und nicht magnolia.public.releases erstellen.
Einen Postprozessor schreiben
Da wir nun mit den Grundlagen vertraut sind, wollen wir ein weiteres Makro erstellen. Diesmal wird der Ablauf anders sein. Anstatt Code in HTML zu konvertieren, werden wir uns in den AsciiDoc-Rendering-Prozess einklinken, um der Seite ein Attribut hinzuzufügen.
Wir möchten, dass das Makro die neueste Modulversion abruft, wenn unsere technischen Redakteure die folgenden Maven-Koordinaten in einem AsciiDoc-Dokument deklarieren:
:group-id: info.magnolia.imaging
:artifact-id: magnolia-imaging
Das Makro soll den Nexus abfragen und die folgende Variable definieren:
:modules-version
Diese Variable kann dann im Dokument referenziert werden, um die neueste Modulversion anzuzeigen.
Um fortzufahren, müssen wir den richtigen Erweiterungstyp wählen. Ein Präprozessor würde den Inhalt aktualisieren, bevor AsciiDoc irgendetwas mit ihm macht. Stattdessen müssen wir zuerst die Seiteneigenschaften lesen. Daher brauchen wir einen Postprozessor, der nach dem Rendering-Prozess läuft.
const request = require('sync-request') function moduleVersionPostprocessor () { this.process((doc, out) => { }) } function register (registry) { registry.postprocessor(moduleVersionPostprocessor) } module.exports.register = register
Die Implementierung der REST-Anfrage und die Verwaltung der Anmeldeinformationen sind ähnlich, aber wir werden die Seitenattribute analysieren:
if (doc.getAttribute('group-id') && doc.getAttribute('artifact-id')) { const groupId = doc.getAttribute('group-id') const artifactId = doc.getAttribute('artifact-id')
So fügen wir das neue Seitenattribut hinzu:
doc.setAttribute('modules-version', version) console.log('Die folgende Version wurde als Seitenattribut definiert: ' + version)
Obwohl wir die Seite nicht ändern, müssen wir ihren Inhalt zurückgeben, damit die Rendering-Pipeline abgeschlossen werden kann:
herausgeben
Dies ist das Endergebnis des Makros:
Die Macht der Antora-Makros
Keines dieser Makros war schwierig zu implementieren. Der Code ist viel einfacher als das Java-Äquivalent in unserer früheren Lösung. Dies beweist, wie leistungsfähig statische Websites sind und dass sich die Migration lohnt.
Für weitere Informationen können Sie sich die gesamten Unterlagen ansehen: