
J'ai construit un framework JavaScript appele Brisa. Le genre de framework qui doit parser chaque fichier source de votre application ; analyser les imports, détecter les composants serveur vs. client, injecter des macros, transformer le JSX. Tout cela se passe au niveau de l'AST.
Avant Brisa, je maintenais déjà next-translate, une librairie i18n pour Next.js. Pour le plugin qui auto-injecte les chargeurs de locale dans les pages, j'utilisais l'API du compilateur TypeScript. Ça marchait. Mais c'etait peniblement lent ; ts.createProgram() pour chaque fichier de page au moment du build, instanciation complete du type-checker, resolution des librairies. Nous avons du ajouter noResolve: true et noLib: true juste pour le rendre supportable. Le parser faisait dix fois plus de travail que nécessaire car tout ce qu'on voulait, c'etait l'AST, pas les types.
Quand j'ai commencé Brisa, je savais qu'il me fallait quelque chose de plus rapide. Quelque chose qui me donne un AST conforme ESTree sans la surcharge d'un compilateur complet. C'est comme ça que j'ai trouve Meriyah.
Pourquoi j'ai choisi Meriyah plutot que tout le reste
Meriyah est écrit entièrement en JavaScript. Pas de bindings natifs. Pas de chargement WASM. Pas d'étape de compilation. Juste parseScript(code, { jsx: true, module: true, next: true }) et vous obtenez un AST ESTree en microsecondes.
Pour le pipeline de build de Brisa, cette différence de vitesse s'accumule. Chaque fichier source d'un projet Brisa passe par Meriyah. Le parser s'exécuté dans AST().parseCodeToAST(), qui d'abord transpile via le transpileur de Bun puis alimente le résultat a Meriyah.
Mais c'est la que ça devient interessant. Brisa à une fonctionnalité appelee renderOn qui permet de pre-rendre des composants au moment du build. Vous ecrivez ceci dans votre page :
<SomeComponent renderOn="build" foo="bar" />
Au moment du build, la transformation AST détecte renderOn="build", remplace le JSX par un appel a __prerender__macro(), et injecte cet import en haut du fichier :
import { __prerender__macro } from 'brisa/macros' with { type: 'macro' };
Ce with { type: 'macro' } est un import attribute qui dit au bundler de Bun de résoudre l'import au moment de la compilation. Le composant est rendu pendant le build, et le résultat est injecte comme HTML statique. L'utilisateur écrit renderOn="build", mais en interne le framework construit des nœuds ImportDeclaration et ImportAttribute de l'AST à la main et regénéré le code.
Le problème : Meriyah ne supportait pas les import attributes quand j'ai commencé à l'utiliser. J'ai donc contribue un PR pour ajouter la fonctionnalité. Ce PR a été accepte, et tout le pipeline de prerendu de Brisa pouvait fonctionner de bout en bout.
Passer de "le parser ne comprend pas ma syntaxe" a "je vais corriger le parser moi-même" est le genre de chose qui n'arrive que lorsqu'on comprend profondement comment les ASTs fonctionnent.
L'inspiration
AST Explorer existe, et c'est genial. Je l'utilisé régulièrement. C'est l'outil de référence pour explorer les ASTs. Je voulais construire quelque chose de similaire dans le cadre de Kitmul ; ma propre version d'un visualiseur AST avec sélection de parsers, vue arborescente interactive et support des parsers que j'utilisé au quotidien.
Le Visualiseur AST fait exactement cela. Collez du JavaScript, choisissez votre parser (Acorn, Meriyah ou SWC), et obtenez un arbre interactif ou du JSON brut. Tout s'exécuté localement.
Le choix du parser compte car chacun produit un AST différent :
- Acorn suit la specification ESTree strictement. C'est le parser qu'ESLint utilisé en interne.
- Meriyah suit aussi ESTree, mais ajoute le support JSX et les fonctionnalités de dernière génération via le flag
next: true. C'est le parser que j'ai choisi pour Brisa car il est rapide, léger et écrit en JS pur. - SWC est un compilateur base sur Rust qui s'exécuté via WASM dans le navigateur. Son AST utilisé une structure différente ;
Moduleau lieu deProgram, des objetsspanau lieu de positionsstart/end.
Trois choses que l'arbre enseigne et que la doc ne fait pas

1. Expressions vs. instructions deviennent visibles.
x = 5;
L'AST montre un ExpressionStatement enveloppant un AssignmentExpression. L'expression est la partie x = 5. L'instruction est l'enveloppe terminee par un point-virgule. Cette distinction explique pourquoi if (x = 5) est du JavaScript valide.
2. La precedence des operateurs devient structurelle.
Parsez 2 + 3 * 4 et vous verrez la multiplication imbriquée dans l'operande droit de l'addition. Le nœud le plus profond s'évalué en premier.
3. Les import attributes revelent comment renderOn="build" fonctionne.
Parsez ceci avec Meriyah :
import { __prerender__macro } from 'brisa/macros' with { type: 'macro' };
Le nœud ImportDeclaration obtient un tableau attributes contenant des nœuds ImportAttribute. Chaque attribut à un key et un value, tous deux des nœuds Literal. C'est l'import que le pipeline de build de Brisa injecte quand il trouve renderOn="build" sur un composant. Le with { type: 'macro' } dit a Bun de résoudre la fonction au moment de la compilation. Sans voir l'arbre, vous ne devineriez jamais que with { type: 'macro' } devient un tableau imbriqué d'objets attribut.
Cas d'utilisation reels en construisant des frameworks
Pipelines de build. Dans Brisa, chaque fichier source est parse en AST, analysé pour les imports, transforme (injection de macros, séparation serveur/client, traitément i18n), et regénéré en code.
Injection de macros de prerendu via renderOn="build". Quand Brisa trouve <Foo renderOn="build" />, la transformation AST construit des nœuds ImportAttribute à la main pour injecter import {__prerender__macro} from 'brisa/macros' with { type: "macro" }. Il y à une subtilite : Meriyah utilisé value sur les nœuds Literal la ou astring attend name. C'est un commentaire reel dans le code source de Brisa : // This astring is looking for "name", but meriyah "value". On ne découvre ce genre de choses qu'en regardant des arbres.
Injection de chargeurs i18n. Dans next-translate-plugin, le loader Webpack utilisé ts.createProgram() pour parser chaque page et détecter ses exports. L'AST TypeScript utilisé des enums SyntaxKind au lieu de types bases sur des chaines.
Resolution de chemins d'import. Brisa resout les imports relatifs en chemins absolus au moment du build. La transformation parcourt les nœuds ImportDeclaration, lit la chaine source.value, la resout, et la remplace.
Comparaison des parsers
| Fonctionnalité | Acorn | Meriyah | SWC |
|---|---|---|---|
| Langage | JavaScript | JavaScript | Rust (WASM dans le navigateur) |
| Specification | ESTree | ESTree | SWC AST |
| Support JSX | Non | Oui | Oui |
| Import attributes | Non | Oui | Oui |
| Vitesse | Rapide | Très rapide | Rapide (après chargement WASM) |
| Taille du bundle | ~120KB | ~320KB | ~14MB (WASM) |
| Utilisé par | ESLint | Brisa | Next.js, Turbopack |

Cinq extraits de code a explorer
Collez-les dans le Visualiseur AST et essayez chaque parser :
1. Arrow function avec retour implicite :
const add = (a, b) => a + b;
2. Import attributes (utilisez Meriyah ou 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. Destructuration avec valeurs par défaut :
const { a = 1, b: { c = 2 } = {} } = config;
Confidentialité
Les trois parsers s'executent entièrement dans votre navigateur. Aucun code n'est transmis à un serveur. La collection Visualiseurs et Outils Logiques inclut des visualiseurs de graphes et des outils regex qui completent bien le travail AST. Le Minuteur Pomodoro avec musique de concentration intégrée est étonnamment efficace pour les sessions d'étude.
La vraie conclusion
Je suis passe de la lutte avec l'API du compilateur TypeScript dans next-translate à la contribution de fonctionnalités au parser Meriyah pour Brisa. Le tournant n'etait pas de lire plus de documentation. C'etait de voir suffisamment d'ASTs pour que les types de nœuds deviennent une seconde nature.
Le Visualiseur AST ne vous apprendra pas la théorie des compilateurs. Il vous apprendra ce que le parser voit quand il lit votre code. Pour écrire des internals de framework, des outils de build, des codemods et des regles ESLint, c'est la seule chose qui compte.
Le Visualiseur AST est gratuit, confidentiel et s'exécuté entièrement dans votre navigateur. Pas d'inscription, pas d'installation, aucune donnée ne quitte votre appareil. Fait partie de la collection Visualiseurs et Outils Logiques sur Kitmul.