
Ich habe ein JavaScript-Framework namens Brisa gebaut. Die Art von Framework, das jede Quelldatei Ihrer Anwendung parsen muss; Imports analysieren, Server- vs. Client-Komponenten erkennen, Macros injizieren, JSX transformieren. All das passiert auf AST-Ebene.
Vor Brisa habe ich bereits next-translate gepflegt, eine i18n-Bibliothek fuer Next.js. Fuer das Plugin, das Locale-Loader automatisch in Seiten injiziert, habe ich die TypeScript Compiler API verwendet. Es funktionierte. Aber es war schmerzhaft langsam; ts.createProgram() fuer jede Seitendatei zur Build-Zeit, vollstaendige Type-Checker-Instanziierung, Lib-Aufloesung. Wir mussten noResolve: true und noLib: true hinzufuegen, nur um es ertraeglich zu machen. Der Parser machte zehnmal mehr Arbeit als noetig, weil alles, was wir wollten, der AST war, nicht die Typen.
Als ich anfing, Brisa zu bauen, wusste ich, dass ich etwas Schnelleres brauchte. Etwas, das mir einen ESTree-konformen AST ohne den Overhead eines vollstaendigen Compilers liefert. So fand ich Meriyah.
Warum ich Meriyah gewaehlt habe
Meriyah ist vollstaendig in JavaScript geschrieben. Keine nativen Bindings. Kein WASM-Ladeschritt. Keine Kompilierung. Nur parseScript(code, { jsx: true, module: true, next: true }) und Sie erhalten einen ESTree-AST in Mikrosekunden.
Fuer Brisas Build-Pipeline summiert sich dieser Geschwindigkeitsunterschied. Jede Quelldatei in einem Brisa-Projekt durchlaeuft Meriyah. Der Parser laeuft in AST().parseCodeToAST(), das zuerst ueber Buns Transpiler transpiliert und dann das Ergebnis an Meriyah weitergibt.
Aber hier wurde es interessant. Brisa hat ein Feature namens renderOn, mit dem Komponenten zur Build-Zeit vorgerendert werden koennen. Man schreibt dies in seiner Seite:
<SomeComponent renderOn="build" foo="bar" />
Zur Build-Zeit erkennt die AST-Transformation renderOn="build", ersetzt das JSX durch einen __prerender__macro()-Aufruf und injiziert diesen Import am Anfang der Datei:
import { __prerender__macro } from 'brisa/macros' with { type: 'macro' };
Dieses with { type: 'macro' } ist ein Import Attribute, das Buns Bundler anweist, den Import zur Kompilierzeit aufzuloesen. Die Komponente wird waehrend des Builds gerendert, und das Ergebnis wird als statisches HTML injiziert. Der Benutzer schreibt renderOn="build", aber intern konstruiert das Framework ImportDeclaration- und ImportAttribute-AST-Knoten von Hand und regeneriert den Code.
Das Problem: Meriyah unterstuetzte keine Import Attributes, als ich anfing, es zu benutzen. Also habe ich einen PR beigetragen, um das Feature hinzuzufuegen. Der PR wurde akzeptiert, und Brisas gesamte Prerender-Pipeline konnte von Anfang bis Ende funktionieren.
Von "der Parser versteht meine Syntax nicht" zu "ich repariere den Parser selbst" zu gelangen, ist die Art von Sache, die nur passiert, wenn man tiefgehend versteht, wie ASTs funktionieren.
Die Inspiration
AST Explorer existiert, und es ist grossartig. Ich benutze es regelmaessig. Es ist das Referenz-Tool zum Erkunden von ASTs. Ich wollte etwas Aehnliches als Teil von Kitmul bauen; meine eigene Version eines AST-Visualizers mit Parser-Auswahl, interaktiver Baumansicht und Unterstuetzung fuer die Parser, die ich taeglich verwende.
Der AST Visualizer macht genau das. JavaScript einfuegen, Parser waehlen (Acorn, Meriyah oder SWC), und einen interaktiven Baum oder rohes JSON erhalten. Alles laeuft lokal im Browser.
Die Parser-Wahl ist wichtig, weil jeder einen leicht anderen AST erzeugt:
- Acorn folgt der ESTree-Spezifikation strikt. Es ist der Parser, den ESLint intern verwendet.
- Meriyah folgt ebenfalls ESTree, fuegt aber JSX-Support und neueste Features ueber das
next: true-Flag hinzu. Es ist der Parser, den ich fuer Brisa gewaehlt habe, weil er schnell, leicht und in purem JS geschrieben ist. - SWC ist ein Rust-basierter Compiler, der via WASM im Browser laeuft. Sein AST verwendet eine andere Struktur;
ModulestattProgram,span-Objekte stattstart/end-Positionen.
Drei Dinge, die der Baum lehrt und die Dokumentation nicht

1. Ausdruecke vs. Anweisungen werden sichtbar.
x = 5;
Der AST zeigt ein ExpressionStatement, das einen AssignmentExpression umschliesst. Diese Unterscheidung erklaert, warum if (x = 5) gueltiges JavaScript ist.
2. Operatorrangfolge wird strukturell.
Parsen Sie 2 + 3 * 4 und die Multiplikation ist innerhalb des rechten Operanden der Addition verschachtelt. Der tiefere Knoten wird zuerst ausgewertet.
3. Import Attributes zeigen, wie renderOn="build" funktioniert.
Parsen Sie dies mit Meriyah:
import { __prerender__macro } from 'brisa/macros' with { type: 'macro' };
Der ImportDeclaration-Knoten erhaelt ein attributes-Array mit ImportAttribute-Knoten. Jedes Attribut hat einen key und einen value, beides Literal-Knoten. Dies ist der Import, den Brisas Build-Pipeline injiziert, wenn sie renderOn="build" auf einer Komponente findet. Das with { type: 'macro' } weist Bun an, die Funktion zur Kompilierzeit aufzuloesen. Ohne den Baum zu sehen, wuerden Sie nie erraten, dass with { type: 'macro' } zu einem verschachtelten Array von Attribut-Objekten wird.
Reale Anwendungsfaelle beim Framework-Bau
Build-Pipelines. In Brisa wird jede Quelldatei zu einem AST geparst, fuer Imports analysiert, transformiert (Macro-Injektion, Server/Client-Trennung, i18n-Verarbeitung) und als Code regeneriert.
Prerender-Macro-Injektion ueber renderOn="build". Wenn Brisa <Foo renderOn="build" /> findet, konstruiert die AST-Transformation ImportAttribute-Knoten von Hand, um import {__prerender__macro} from 'brisa/macros' with { type: "macro" } zu injizieren. Es gibt eine Eigenheit: Meriyah verwendet value bei Literal-Knoten, wo astring name erwartet. Das ist ein tatsaechlicher Kommentar im Brisa-Quellcode: // This astring is looking for "name", but meriyah "value". So etwas entdeckt man nur, wenn man Baeume studiert.
i18n-Loader-Injektion. In next-translate-plugin verwendet der Webpack-Loader ts.createProgram() zum Parsen jeder Seite. Der TypeScript-AST verwendet SyntaxKind-Enums statt string-basierter Typen.
Import-Pfad-Aufloesung. Brisa loest relative Imports zur Build-Zeit in absolute Pfade auf. Die Transformation durchlaeuft ImportDeclaration-Knoten und ersetzt source.value.
Parser-Vergleich
| Funktion | Acorn | Meriyah | SWC |
|---|---|---|---|
| Sprache | JavaScript | JavaScript | Rust (WASM im Browser) |
| Spezifikation | ESTree | ESTree | SWC AST |
| JSX-Support | Nein | Ja | Ja |
| Import Attributes | Nein | Ja | Ja |
| Geschwindigkeit | Schnell | Sehr schnell | Schnell (nach WASM-Ladung) |
| Bundle-Groesse | ~120KB | ~320KB | ~14MB (WASM) |
| Verwendet von | ESLint | Brisa | Next.js, Turbopack |

Fuenf Code-Snippets zum Erkunden
Fuegen Sie diese in den AST Visualizer ein und probieren Sie jeden Parser:
1. Arrow Function mit implizitem Return:
const add = (a, b) => a + b;
2. Import Attributes (verwenden Sie Meriyah oder SWC):
import { __prerender__macro } from 'brisa/macros' with { type: 'macro' };
3. Optional Chaining:
const value = obj?.nested?.deep?.property;
4. Async/await:
async function fetchData() {
const response = await fetch('/api/data');
return response.json();
}
5. Destrukturierung mit Standardwerten:
const { a = 1, b: { c = 2 } = {} } = config;
Datenschutz
Alle drei Parser laufen vollstaendig in Ihrem Browser. Kein Code wird an einen Server uebertragen. Die Sammlung Visualisierer und Logik-Tools umfasst Graph-Visualisierer und Regex-Tools, die gut zur AST-Arbeit passen. Der Pomodoro Timer mit integrierter Fokus-Musik ist ueberraschend effektiv fuer Lernsessions.
Die eigentliche Erkenntnis
Ich bin von der TypeScript Compiler API in next-translate zur Contribution von Parser-Features an Meriyah fuer Brisa gegangen. Der Wendepunkt war nicht mehr Dokumentation lesen. Es war, genug ASTs zu sehen, bis die Knotentypen zur zweiten Natur wurden.
Der AST Visualizer wird Ihnen keine Compiler-Theorie beibringen. Er wird Ihnen zeigen, was der Parser sieht, wenn er Ihren Code liest. Fuer Framework-Internals, Build-Tools, Codemods und ESLint-Regeln ist das das Einzige, was zaehlt.
Der AST Visualizer ist kostenlos, privat und laeuft vollstaendig in Ihrem Browser. Keine Anmeldung, keine Installation, keine Daten verlassen Ihr Geraet. Teil der Visualisierer und Logik-Tools Sammlung auf Kitmul.