Templates, die zu einfachen Funktionen kompiliert werden
Verso macht aus einem Template beim Build eine JavaScript-Funktion. Kein virtuelles DOM, keine Laufzeitabhängigkeiten, kein Parser im Client — nur eine Funktion, die Daten nimmt und einen String zurückgibt.
<!-- greeting.vs -->
<section class="cards">
{{#each items as item, i}}
<article data-index="{{i}}">
<h3>{{item.title}}</h3>
{{#if item.note}}
<p>{{item.note | truncate: 120}}</p>
{{/if}}
</article>
{{#else}}
<p>Nothing here yet.</p>
{{/each}}
</section>
Für alle, die HTML auf dem Server rendern
Verso entstand 2021 als 300-Zeilen-Datei in einer Content-Pipeline, die der String-Verkettung entwachsen war und kein Framework wollte. Es ist immer noch bewusst klein.
Kompiliert, nicht interpretiert
Jedes Template wird zu einer eigenständigen Funktion ohne Closure über die Engine. Eine Tabelle mit 2 000 Zeilen rendert auf einem Laptop von 2019 in 1,9 ms — etwa so lange, wie die String-Allokation selbst dauert.
Nichts darunter
Keine Abhängigkeiten zur Laufzeit und insgesamt drei Entwicklungsabhängigkeiten. Das ganze Paket besteht aus vier Dateien, und npm ls gibt nach einer frischen Installation eine einzige Zeile aus.
Streamt von Haus aus
renderToStream() gibt jeden Block der obersten Ebene aus, sobald er fertig ist — ein langsamer Datenbankaufruf verzögert so nur einen Abschnitt statt der ganzen Seite.
Escaped von Haus aus
Jede Interpolation wird HTML-escaped, solange Sie sich nicht ausdrücklich mit {{{ }}} dagegen entscheiden. Das Escaping berücksichtigt den Kontext von Attributen, URLs und Inline-Styles.
Lesbare Fehler
Kompilierfehler enthalten Template-Pfad, Zeile, Spalte und ein Caret unter dem fehlerhaften Token. Laufzeitfehler behalten den Template-Frame im Stacktrace.
Absichtlich stabil
Eine einzige inkompatible Änderung in vier Jahren, sechs Monate vorher angekündigt. Die 2.x-Linie bekommt noch bis Juni 2027 Sicherheitsfixes.
Dreißig Sekunden bis zum ersten Rendern
# npm, pnpm or yarn — all the same package
$ npm install verso
# or try it without installing
$ npx verso render greeting.vs --data data.json
Benötigt Node.js 18 oder neuer. Der Browser-Build heißt verso/browser und enthält nur die Laufzeit-Helfer — der Compiler bleibt auf Ihrer Build-Maschine.
import { compileFile } from 'verso';
const page = await compileFile('./views/page.vs');
server.get('/', async (req, res) => {
res.type('html').send(page({
title: 'Release notes',
items: await db.releases(20)
}));
});
Gemessen, nicht behauptet
Benchmarks laufen auf Node 22.11, Apple M1, 50 Iterationen nach 20 Aufwärmdurchläufen. Die Suite liegt in bench/, und die Rohausgabe wird mit jedem Release eingecheckt.
Wie aus einem Template eine Funktion wird
1 · Parsen
Ein Scanner mit einem einzigen Durchlauf erzeugt eine flache Token-Liste. Im Parser gibt es nirgends Backtracking regulärer Ausdrücke, also scheitert ein fehlerhaftes Template in linearer Zeit, statt hängenzubleiben.
2 · Aufbauen
Aus den Tokens wird ein kleiner Knotenbaum. Konstante Teilbäume — statisches HTML, literale Attribute — werden an dieser Stelle zu einzelnen String-Blöcken zusammengefaltet.
3 · Ausgeben
Der Baum wird als JavaScript-Quelltext ausgegeben und an new Function übergeben oder von der CLI auf die Platte geschrieben, wenn Sie ihn ansehen oder bündeln wollen.
Leise, an ein paar Stellen
Verso ist kein populäres Projekt und will auch keins sein. Es wird etwa 40 000-mal im Monat heruntergeladen, meist von denselben Build-Pipelines, und das ist eine Größe, die wir ehrlich pflegen können.
Wenn Sie es gegen etwas Größeres abwägen: Nehmen Sie Verso, wenn Sie HTML auf dem Server rendern, die ganze Engine an einem Nachmittag lesen und keinen Abhängigkeitsbaum prüfen wollen. Nehmen Sie etwas anderes, wenn Sie clientseitige Reaktivität brauchen — Verso hat keine Meinung zum DOM und wird nie eine haben.