
J'ai construit un framework JavaScript appele Brisa. Le genre de framework qui doit parser chaque fichier source de votre application ; analyser les imports, detecter les composants serveur vs. client, injecter des macros, transformer le JSX. Tout cela se passe au niveau de l'AST.
Avant Brisa, je maintenais deja 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. Ca 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 necessaire car tout ce qu'on voulait, c'etait l'AST, pas les types.
Quand j'ai commence 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 ca que j'ai trouve Meriyah.
Pourquoi j'ai choisi Meriyah plutot que tout le reste
Meriyah est ecrit entierement en JavaScript. Pas de bindings natifs. Pas de chargement WASM. Pas d'etape 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 difference de vitesse s'accumule. Chaque fichier source d'un projet Brisa passe par Meriyah. Le parser s'execute dans AST().parseCodeToAST(), qui d'abord transpile via le transpileur de Bun puis alimente le resultat a Meriyah.
Mais c'est la que ca devient interessant. Brisa a une fonctionnalite 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 detecte 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 resoudre l'import au moment de la compilation. Le composant est rendu pendant le build, et le resultat est injecte comme HTML statique. L'utilisateur ecrit renderOn="build", mais en interne le framework construit des noeuds ImportDeclaration et ImportAttribute de l'AST a la main et regenere le code.
Le probleme : Meriyah ne supportait pas les import attributes quand j'ai commence a l'utiliser. J'ai donc contribue un PR pour ajouter la fonctionnalite. Ce PR a ete 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-meme" 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'utilise regulierement. C'est l'outil de reference pour explorer les ASTs. Je voulais construire quelque chose de similaire dans le cadre de Kitmul ; ma propre version d'un visualiseur AST avec selection de parsers, vue arborescente interactive et support des parsers que j'utilise 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'execute localement.
Le choix du parser compte car chacun produit un AST different :
- Acorn suit la specification ESTree strictement. C'est le parser qu'ESLint utilise en interne.
- Meriyah suit aussi ESTree, mais ajoute le support JSX et les fonctionnalites de derniere generation via le flag
next: true. C'est le parser que j'ai choisi pour Brisa car il est rapide, leger et ecrit en JS pur. - SWC est un compilateur base sur Rust qui s'execute via WASM dans le navigateur. Son AST utilise une structure differente ;
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 imbriquee dans l'operande droit de l'addition. Le noeud le plus profond s'evalue 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 noeud ImportDeclaration obtient un tableau attributes contenant des noeuds ImportAttribute. Chaque attribut a un key et un value, tous deux des noeuds 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 resoudre la fonction au moment de la compilation. Sans voir l'arbre, vous ne devineriez jamais que with { type: 'macro' } devient un tableau imbrique d'objets attribut.
Cas d'utilisation reels en construisant des frameworks
Pipelines de build. Dans Brisa, chaque fichier source est parse en AST, analyse pour les imports, transforme (injection de macros, separation serveur/client, traitement i18n), et regenere en code.
Injection de macros de prerendu via renderOn="build". Quand Brisa trouve <Foo renderOn="build" />, la transformation AST construit des noeuds ImportAttribute a la main pour injecter import {__prerender__macro} from 'brisa/macros' with { type: "macro" }. Il y a une subtilite : Meriyah utilise value sur les noeuds 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 decouvre ce genre de choses qu'en regardant des arbres.
Injection de chargeurs i18n. Dans next-translate-plugin, le loader Webpack utilise ts.createProgram() pour parser chaque page et detecter ses exports. L'AST TypeScript utilise 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 noeuds ImportDeclaration, lit la chaine source.value, la resout, et la remplace.
Comparaison des parsers
| Fonctionnalite | 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 | Tres rapide | Rapide (apres chargement WASM) |
| Taille du bundle | ~120KB | ~320KB | ~14MB (WASM) |
| Utilise 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 defaut :
const { a = 1, b: { c = 2 } = {} } = config;
Confidentialite
Les trois parsers s'executent entierement dans votre navigateur. Aucun code n'est transmis a 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 integree est etonnamment efficace pour les sessions d'etude.
La vraie conclusion
Je suis passe de la lutte avec l'API du compilateur TypeScript dans next-translate a la contribution de fonctionnalites 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 noeuds deviennent une seconde nature.
Le Visualiseur AST ne vous apprendra pas la theorie des compilateurs. Il vous apprendra ce que le parser voit quand il lit votre code. Pour ecrire 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'execute entierement dans votre navigateur. Pas d'inscription, pas d'installation, aucune donnee ne quitte votre appareil. Fait partie de la collection Visualiseurs et Outils Logiques sur Kitmul.