Eine Sprache hinzufügen, die Shiki nicht kennt
بِسْمِ ٱللَّهِ ٱلرَّحْمَـٰنِ ٱلرَّحِيمِ
Das Problem, das du nicht bemerkst
Diese Seite hat zwei Blogposts über Caddy. Beide nutzen umzäunte Code-Blöcke mit ```caddy, um Caddyfile-Konfiguration zu zeigen. Expressive Code rendert sie, Shiki tokenisiert sie, die Seite baut ohne Fehler.
Nur wurde jeder Caddy-Block als Plaintext gerendert. Kein Syntax-Highlighting, keine Farbe, keine visuelle Struktur. Nur monochrome Zeichen auf dunklem Grund.
Die Blöcke sahen aus wie Code. Sie hatten Zeilennummern, den Rahmen, den Copy-Button. Alles, was Expressive Code beisteuert, war da. Was fehlte, war das, was Shiki beisteuert: das eigentliche Highlighting. Direktiven, Strings, Kommentare, Werte, alles in derselben Farbe.
Aufgefallen ist es im direkten Vergleich des Blogs mit VS Code. Dieselbe Caddy-Config, im Editor in vollen Farben, auf der Seite monochrom.
Warum das passiert
Expressive Code nutzt Shiki für Syntax-Highlighting, und Shiki bringt Grammatiken für über 250 Sprachen mit. JavaScript, Python, TypeScript, Bash, HTML, CSS, Go, Rust. Die Sprachen, über die die meisten technischen Blogs schreiben.
Caddy gehört nicht dazu. Traefik, HAProxy oder Docker Compose auch nicht, also Tools, die in selbstgehosteter Infrastruktur ständig auftauchen. Wenn Shiki auf einen Sprach-Identifier trifft, den es nicht erkennt, fällt es auf Plaintext zurück. Kein Fehler, kein Build-Abbruch. Nur eine Warnung tief im Build-Output, die du vermutlich nicht liest:
[astro-expressive-code] Error while highlighting code block using language "caddy".The language could not be found. Using "txt" instead.Das Wort „Error“ ist großzügig gewählt. Nichts geht kaputt. Die Seite rendert. Der Code-Block sieht aus wie ein Code-Block. Er macht nur das eine nicht, wofür ein Syntax-Highlighter da ist.
Wo Grammatiken herkommen
Shiki nutzt TextMate-Grammatiken, dasselbe Format wie VS Code für Syntax-Highlighting. Jede VS-Code-Sprach-Erweiterung liefert eine .tmLanguage.json-Datei mit, die definiert, wie die Sprache tokenisiert wird: was ein Keyword ist, was ein String, was ein Kommentar.
Wenn VS Code deine Sprache korrekt highlightet, existiert die Grammatik bereits. Sie liegt in deinem Extensions-Ordner.
Für Caddy ist die offizielle Erweiterung caddyserver/vscode-caddyfile, veröffentlicht als matthewpi.caddyfile-support. MIT-lizenziert.
Die Grammatikdatei liegt unter:
~/.vscode/extensions/matthewpi.caddyfile-support-0.4.0/syntaxes/caddyfile.tmLanguage.jsonDieselbe Datei, die VS Code Caddyfiles highlighten lässt, kann das auch Expressive Code beibringen. Ins Projekt kopieren, die Config darauf zeigen lassen, fertig.
Fünf Zeilen Config
Expressive Code akzeptiert eigene Grammatiken über die shiki.langs-Option in ec.config.mjs:
import { defineEcConfig } from 'astro-expressive-code'import fs from 'node:fs'
const caddyfile_Grammar = { ...JSON.parse(fs.readFileSync('./src/Scripts/Build/Grammars/caddyfile.tmLanguage.json', 'utf-8')), name: 'caddy',}
export default defineEcConfig({ shiki: { langs: [caddyfile_Grammar], langAlias: { caddyfile: 'caddy' }, },})Grammatik laden, in langs aufnehmen, optional alternative Namen aliasen. Der Rest der Config bleibt wie er ist.
Site bauen. Keine Warnungen mehr. Code-Blöcke leuchten auf.
Warum es trotzdem nicht funktionierte
Nur taten sie das nicht. Nicht beim ersten Versuch.
Die Grammatik lud ohne Fehler. Der Build lief sauber durch. Die Code-Blöcke wurden weiterhin als Plaintext gerendert. Keine Warnungen, kein Hinweis darauf, dass etwas nicht stimmte.
Das Problem war das name-Feld der Grammatik. Die Caddyfile-Grammatik deklariert sich als name: "Caddyfile", großes C, großes F. Die umzäunten Code-Blöcke in den MDX-Dateien nutzen aber ```caddy, alles klein. Shiki vergleicht Sprachnamen case-sensitive. "Caddyfile" matcht nicht "caddy".
Die langAlias-Option sah nach dem Fix aus. caddy auf caddyfile mappen. Aber der Alias wird aufgelöst, bevor die geladenen Sprachen geprüft werden, und die Grammatik war unter "Caddyfile" registriert, nicht unter "caddyfile". Der Alias zeigte auf einen Namen, den es nicht gab.
Der Fix: das name-Property der Grammatik beim Laden überschreiben.
const caddyfile_Grammar = { ...JSON.parse(fs.readFileSync('./path/to/caddyfile.tmLanguage.json', 'utf-8')), name: 'caddy', // override "Caddyfile" to match ```caddy fences}Eine Zeile. Die Grammatik registriert sich jetzt als "caddy", die Fences sagen caddy, Shiki findet einen Treffer. Alles läuft.
Das ist die Sorte Bug, die sich nicht ankündigt. Kein Stack-Trace, keine Fehlermeldung, kein gescheiterter Build. Die Grammatik lädt erfolgreich. Shiki verbindet sie nur nie mit deinen Code-Blöcken. Du musst wissen, dass Shikis Namensvergleich case-sensitive ist, und du musst wissen, dass der interne Name der Grammatik nicht zwingend zu dem Identifier passt, den du in deinem Markdown verwendest. Beides steht nicht im Setup-Guide von Expressive Code.
Das Muster
Eine eigene Sprache zu Expressive Code hinzuzufügen läuft in drei Schritten ab:
-
Grammatik finden. Sieh in deinem VS-Code-Extensions-Ordner nach. Wenn dein Editor die Sprache highlightet, existiert die
.tmLanguage.json. Kopiere sie ins Projekt, damit der Build nicht von deinem lokalen Editor-Setup abhängt. Lizenz prüfen. Die einkopierte Version ist deine: Upstream-Fixes erreichen deinen Build erst wieder, wenn du die Datei erneut kopierst. -
Registrieren. In deiner Expressive-Code-Config zu
shiki.langshinzufügen. MitlangAliasarbeiten, wenn mehrere Identifier auf dieselbe Grammatik zeigen sollen. -
Namen abgleichen. Das
name-Property der Grammatik so überschreiben, dass es zu dem passt, was du in deinen umzäunten Code-Blöcken verwendest. Geh nicht davon aus, dass der interne Name der Grammatik zu deinem Fence-Identifier passt.
Der ganze Vorgang ist kurz, sobald man ihn kennt. Die eigentliche Arbeit liegt darin herauszufinden, dass man ihn überhaupt braucht und warum der erste Versuch stillschweigend scheitert.
Wie es jetzt aussieht
Vorher:
handle_errors { rewrite * /404.html file_server}Nachher:
handle_errors { rewrite * /404.html file_server}Gleicher Inhalt. Der Unterschied liegt darin, ob das Auge des Lesers die Struktur auf einen Blick erfasst oder jedes Wort lesen muss. Bei einem Post, der jemandem Caddy beibringen will, ist das inshallah der Unterschied zwischen nützlich und frustrierend.
Code-Blöcke auf einem technischen Blog sind ein leises Glaubwürdigkeitssignal. Wenn sie mit vollem Highlighting rendern, fällt es niemandem auf. Wenn nicht, fällt es genau den Leuten auf, von denen du willst, dass sie deine Arbeit lesen.
Luft- und Raumfahrtingenieur
Ethischer Unternehmer in der Öffentlichkeit
Sie kümmern sich um Ihr Geschäft
Ich kümmere mich um die digitale Seite
Zusammenarbeiten
- KI mit Ehrlichkeit
- Private Infrastruktur
- Websites die performen
Erzählen Sie mir von Ihrer Situation:
javed@javedab.com Mehr über mich