← Retour aux articles
25 February 2026 5 min de lecture GoHTMXVueCompilateurPerformance

GMX : mon transpileur Go + HTMX inspiré de Vue

J'ai écrit un transpileur qui compile des single-file components en binaire Go natif + HTMX. 14 000 req/s, p99 à 2 ms, zéro JS. Une preuve de concept née de la curiosité.

🔧 J’ai construit mon propre framework. Pour le fun.

Go au quotidien, Vue en front depuis des années. J’adore la DX des single-file components, la Composition API, tout colocalisé dans un .vue, c’est propre.

Mais les SPA… c’est lent. Hydration, bundle JS de 400ko, loading spinners partout.
Le SSR c’est mieux, mais on maintient deux runtimes pour le prix d’un.

Et puis il y avait HTMX m’intriguait depuis un moment.
Le serveur renvoie du HTML, le navigateur l’affiche.
Pas de build JS, pas de state client, pas de virtual DOM.
Mais je n’avais jamais pris le temps de vraiment creuser.

Et puis je me suis posé une question idiote : est-ce qu’on pourrait écrire des single-file components à la Vue, mais qui compilent en Go + HTMX ? 🫨

Un seul fichier. Un seul build. Un seul binaire.

J’avais jamais écrit de transpileur. Le défi était trop fun pour ne pas essayer.

GMX Script

Le résultat s’appelle GMX. C’est un langage déclaratif, syntaxe proche de Go, structure inspirée de Vue, qui compile en code Go natif avec HTMX.

Voici à quoi ressemble un fichier .gmx :

<script>
service Database {
  provider: "sqlite"
  url:      string @env("DATABASE_URL")
}

model Task {
  id:    uuid    @pk @default(uuid_v4)
  title: string  @min(3) @max(255)
  done:  bool    @default(false)
}

func toggleTask(id: uuid) error {
  let task = try Task.find(id)
  task.done = !task.done
  try task.save()
  return render(task)
}

func createTask(title: string) error {
  if title == "" {
    return error("Title cannot be empty")
  }
  const task = Task{title: title, done: false}
  try task.save()
  return render(task)
}
</script>

<template>
<ul id="task-list">
  {{range .Tasks}}
  <li class="task-item {{if .Done}}done{{end}}" id="task-{{.ID}}">
    <input type="checkbox" {{if .Done}}checked{{end}}
      hx-patch="{{route "toggleTask"}}?id={{.ID}}"
      hx-target="#task-{{.ID}}"
      hx-swap="outerHTML" />
    <span>{{.Title}}</span>
  </li>
  {{end}}
</ul>
</template>

Si vous faites du Go et du Vue, vous êtes en terrain connu.
Le <script> contient le modèle de données et la logique métier dans une syntaxe proche de Go.
Le <template> c’est du HTML avec des Go templates et des attributs HTMX.
Le tout dans un seul fichier.

Quelques quick wins qui rendent le truc agréable :

  • try fait le error check + return automatiquement : fini les 3 lignes de if err != nil à chaque appel
  • {{route "toggleTask"}} est validé à la compilation : une route qui n’existe pas, c’est une erreur de build, pas un 404 en prod
  • Protection CSRF automatique : double-submit cookie pattern, zéro config
  • @scoped pour le multi-tenant : toutes les requêtes sont scopées automatiquement au tenant courant

Et à la fin :

gmx demo.gmx main.go
go build -o app main.go
./app
# → Serveur web complet avec SQLite, CSRF, handlers HTMX

Un fichier en entrée, un binaire en sortie. Le binaire embarque tout : serveur HTTP, templates, migrations. Vous le copiez sur un serveur, vous le lancez, c’est en ligne.

Le pipeline

Sous le capot, GMX suit un pipeline classique de compilateur :

.gmx → Lexer → Parser → AST → Transpileur → Code Go → go build → Binaire
  1. Lexer — tokenise le .gmx, sépare les blocs <script> et <template>, identifie les mots-clés (model, service, func, try…)
  2. Parser — construit l’AST (arbre de syntaxe abstraite). Les erreurs de syntaxe tombent ici, pas en prod
  3. Transpileur — convertit l’AST en Go valide. Les model deviennent des structs GORM, les fonctions deviennent des handlers net/http, les try deviennent des if err != nil { return err }
  4. Générateur — assemble le code Go complet : main.go, routes, migrations, templates embarqués
  5. go build — et le compilateur Go fait le reste

Écrire un transpileur, c’était un premier pour moi. Claude a été un accélérateur incroyable là-dessus — le boilerplate du lexer, les cas limites du parser, les tests unitaires sur 50 variations de syntaxe. Ça m’a permis de me concentrer sur l’architecture du pipeline et les choix de design, au lieu de passer trois semaines à debug des edge cases de tokenisation. Résultat : un POC fonctionnel avec 81% de couverture de tests, en une fraction du temps que j’aurais mis seul.

Les chiffres

Parce que la vraie question c’est : est-ce que ça tient la route ?

Benchmark sur une todo app, SQLite, single core :

StackRead req/sWrite req/sp99 readRAMJS bundle
GMX (Go+HTMX)~14 000~3151-2 ms~102 MB0 KB
Next.js (SSR)~2 000-4 000~200-50015-50 ms~150-300 MB80-200 KB
Rails (Hotwire)~800-1 500~200-40020-80 ms~150-250 MB~15 KB
Django (HTMX)~500-1 200~150-35030-100 ms~80-150 MB0 KB
Laravel (Livewire)~600-1 000~150-30025-80 ms~100-200 MB~30 KB
Phoenix (LiveView)~10 000-15 000~3 000-5 0002-5 ms~50-80 MB~15 KB

14 000 req/s en lecture. p99 à 1-2 ms. Zéro kilo-octet de JavaScript envoyé au client. 102 Mo de RAM — moins qu’un onglet Chrome.

Le bottleneck en écriture (~315 req/s), c’est SQLite et son write lock global, pas Go. Avec PostgreSQL, ce chiffre monterait à ~5 000+ req/s.

Phoenix reste devant en écriture grâce à la BEAM — Erlang a 30 ans d’optimisation sur la concurrence, c’est juste pas la même catégorie. Mais en lecture et en latence, GMX joue dans la même cour. Pour un POC, je suis plutôt content.

Un POC, pas un framework

Soyons clairs : GMX est une preuve de concept. C’est la réponse à “est-ce qu’on peut avoir le DX des single-file components Vue avec les performances de Go, en virant le JavaScript du navigateur ?”

La réponse est oui. Et c’est le projet le plus fun que j’ai fait depuis longtemps.

Ce n’est pas un remplacement pour Vue ou React. Ce n’est pas un framework prêt pour la prod. C’est une exploration — un terrain de jeu pour tester HTMX sérieusement, apprendre à écrire un compilateur, et voir ce qui se passe quand on fusionne deux mondes qui n’étaient pas censés se rencontrer.

Si le concept vous intrigue, allez jeter un œil.

btouchard.github.io/gmx

Besoin d'aide sur ce sujet ?

Réserver un créneau