Por qué Astro tiene un compilador propio
Por qué Astro no pudo reutilizar un compilador existente y necesitó el suyo. El formato .astro como dos zonas —el frontmatter de servidor y la plantilla, un superset de HTML— y por qué eso no es JSX ni se compila con la cadena de JavaScript. Qué produce ese compilador además de una función de render, y el salto de Go a Rust que estrena Astro 7.
Un archivo .astro se parece tanto a HTML con una cabecera de JavaScript que es fácil olvidar la decisión que hay detrás: Astro no podía reutilizar ningún compilador existente. Ni Babel, ni tsc, ni la transformación de JSX servían, porque .astro no es un lenguaje de expresiones incrustado en JavaScript sino un superset de HTML con una zona de servidor cosida delante. Esa forma —HTML como anfitrión, no como invitado— obliga a un analizador propio, y ese analizador, escrito en Go durante años, es lo que Astro 7 acaba de reescribir en Rust. Entender por qué existe ese compilador es entender qué es de verdad un componente .astro.
- Describir el formato
.astrocomo dos zonas separadas por la valla---. - Explicar por qué no es JSX y no se compila con la cadena de JavaScript.
- Justificar la existencia de un compilador dedicado y qué produce.
- Situar el salto de Go a Rust que trae Astro 7 y qué gana.
Dos zonas separadas por una valla
Un componente .astro se parte en dos mitades de naturaleza opuesta, y la frontera entre ellas es sintáctica, no decorativa. Arriba, entre dos vallas de tres guiones, vive el frontmatter: código que corre una sola vez, en el servidor, durante el render —imports, await de nivel superior, lectura de props, cálculo de datos—. Debajo vive la plantilla: marcado que el componente emite, un superset de HTML donde las expresiones de JavaScript entran acotadas entre llaves.
---
// zona de frontmatter: corre en el servidor, una vez
import Boton from '../components/Boton.astro';
const usuario = await fetch('/api/yo').then((r) => r.json());
---
<h1>Hola, {usuario.nombre}</h1>
<Boton>Entrar</Boton>
La valla --- no es un adorno prestado del Markdown: es un token que el compilador reconoce para saber dónde termina el mundo del servidor y empieza el del marcado. Cada zona sufre un destino opuesto en la compilación:
- El frontmatter se convierte en el cuerpo de una función que corre en el servidor y luego desaparece: ni una línea suya llega al navegador.
- La plantilla se convierte en salida: HTML casi literal con huecos donde se interpolan las expresiones que evaluó el frontmatter.
Esa asimetría —un lado que corre y otro que se pinta— es imposible de expresar en un lenguaje donde todo es la misma clase de expresión, y es la primera razón por la que Astro necesitaba analizar el archivo a su manera. Un compilador de JavaScript no tiene ni el concepto de una zona que se ejecuta y otra que se emite: para él, todo es código o todo es dato, nunca las dos cosas cosidas por una valla.
Es fácil imaginar que las expresiones de la plantilla vuelven a ejecutar el frontmatter, pero no: el frontmatter corre una sola vez cuando el componente se renderiza, y las llaves de abajo solo leen los valores que ya calculó. Esa separación entre un cómputo único arriba y muchas lecturas abajo es parte de lo que el compilador tiene que respetar al generar el módulo, y no se parece en nada al modelo de reejecución de un componente de framework.
Por qué esto no es JSX
La tentación de asimilar .astro a JSX se deshace en cuanto se mira la dirección de cada uno. En JSX, el anfitrión es JavaScript y el marcado es el invitado: cada etiqueta se compila a una llamada como jsx() o React.createElement, y el archivo entero es, al final, una expresión de JavaScript. En .astro la relación se invierte: el anfitrión es HTML y JavaScript es el invitado que entra solo dentro de las llaves. No hay createElement que valga; hay HTML de verdad que se conserva casi tal cual.
// JSX: el marcado ES JavaScript, se traduce a llamadas de funcion
function Saludo({ nombre }) {
return <h1>Hola, {nombre}</h1>; // -> jsx('h1', { children: ['Hola, ', nombre] })
}
En JSX ese <h1> no sobrevive como texto: es azúcar sintáctico sobre una llamada, y el compilador de JSX lo demuestra convirtiéndolo en jsx('h1', ...). En .astro, el mismo <h1> de la plantilla se conserva como HTML y solo {nombre} se trata como código. Dos gramáticas con el peso repartido al revés.
HTML es el anfitrion
En JSX el marcado se traduce a llamadas de funcion. En .astro el HTML se conserva como texto y solo las expresiones se evaluan.
Sin raiz unica
Un .astro emite varios nodos hermanos sin envolverlos. JSX exige un unico elemento raiz o un fragmento.
Atributos de HTML
Se escribe class y for como en HTML, no className ni htmlFor. El compilador respeta la gramatica del navegador.
Await arriba
El frontmatter admite await de nivel superior porque es codigo de servidor, algo ajeno por completo a JSX.
Las diferencias no son cosméticas. En .astro el espacio en blanco es significativo como en HTML, los comentarios son comentarios de HTML, el doctype se respeta, y las etiquetas <script> y <style> no son elementos cualquiera sino piezas que el compilador iza y procesa aparte. Nada de eso cabe en la semántica de JSX, donde <style> sería un nodo React más y el espacio se colapsa. Un compilador de JSX aplicado a un .astro no produciría un error: produciría algo semánticamente equivocado, que es peor.
La forma más rápida de recordar la diferencia es preguntarse quién manda. En JSX manda JavaScript y el HTML es una expresión más dentro de él; por eso todo acaba en una llamada de función. En .astro manda el HTML y JavaScript solo aparece invitado entre llaves o en el frontmatter; por eso el marcado sobrevive casi literal hasta la salida. Compilar uno con las reglas del otro rompe justo esa jerarquía.
Un compilador dedicado, y qué produce
Reutilizar tsc o Babel era inviable porque ninguno sabe analizar una gramática con anfitrión HTML, valla de frontmatter, estilos con ámbito e islas incrustadas. Pero hay una razón más profunda: el compilador de Astro no emite solo una función de render. De cada .astro extrae además un puñado de metadatos que el resto del framework necesita. Un transformador de JavaScript devuelve código; el de Astro devuelve código y un informe sobre lo que ese código significa.
Ese informe es justo lo que ninguna herramienta ajena sabría producir:
- Los estilos del componente, izados y sellados con su hash de ámbito.
- Los scripts que hay que procesar y empaquetar aparte.
- La lista de islas —los componentes con directiva
client:— que habrá que hidratar. - El identificador del componente, la semilla del hash que aísla sus estilos.
flowchart TD SRC[archivo punto astro] --> FM[zona frontmatter] SRC --> TPL[zona plantilla] FM --> COMP[compilador de astro] TPL --> COMP COMP --> RF[funcion de render] COMP --> META[metadatos estilos islas y scope] RF --> OUT[modulo javascript] META --> OUT
Por eso el compilador es la pieza que define qué es un componente .astro. No hay una especificación del formato al margen del programa que lo procesa: el lenguaje es lo que el compilador acepta y traduce. Cambiar una regla de la gramática es cambiar el compilador, y ninguna herramienta ajena podría seguir ese contrato sin reimplementarlo entero. Tener compilador propio no es un lujo de ingeniería: es la única forma de que .astro exista como formato con identidad.
La consecuencia práctica es que el editor, el formateador y el servidor de lenguaje que usas con Astro no adivinan la sintaxis: se apoyan en ese mismo compilador para analizarla. Cuando tu editor resalta un error en un .astro antes de compilar, por debajo está corriendo el parse del compilador sobre tu archivo. El compilador no es solo una etapa del build; es la fuente única de verdad sobre el formato, compartida por todas las herramientas que lo tocan.
Lo que el compilador deja fuera
Para no atribuirle poderes que no tiene, conviene marcar su frontera. El compilador de Astro traduce un .astro en un módulo con sus metadatos, y ahí acaba su mandato. No hace varias cosas que suelen confundirse con su trabajo:
- No empaqueta: reunir módulos, resolver
node_modulesy producir los bundles finales es tarea de Vite y su bundler Rolldown. - No minifica ni optimiza el JavaScript de salida; solo lo genera legible.
- No resuelve los imports a rutas finales con hash; eso ocurre después, en la fase de build.
Distinguir esa frontera evita un malentendido caro: cuando algo va mal, saber si el problema es del compilador —la traducción de tu .astro— o del bundler —el empaquetado posterior— es la mitad del diagnóstico. Cada pieza tiene su territorio, y este nivel trata solo de la primera.
Del Go al Rust en Astro 7
Durante casi toda su historia ese compilador estuvo escrito en Go y se distribuía compilado a WebAssembly, para poder invocarlo desde el mundo de Node. Funcionaba, pero pagaba un peaje en cada archivo al cruzar la frontera entre JavaScript y WASM. Astro 7 lo reescribe en Rust, el lenguaje hacia el que converge todo el tooling nativo de la web —los parsers, los bundlers, los linters de nueva generación—. La sintaxis .astro no cambia ni una coma; lo que cambia es la velocidad con la que se analiza y el hecho de compartir cimientos con el resto de la cadena de build.
El cambio, invisible en tu código, se nota en tres frentes que las próximas lecciones desarrollan:
- Distribución: de un
.wasmuniversal a binarios nativos por plataforma, con WASM como red de seguridad. - Rendimiento: desaparece el coste de serializar cada
.astroal cruzar hacia WebAssembly. - Integración: el compilador comparte lenguaje y estructuras con Rolldown y el resto del tooling nativo.
Ninguno de esos tres frentes te pide reaprender nada; los tres, sumados, son la razón de que un sitio de decenas de miles de páginas pase de tardar minutos a tardar segundos.
Un salto tan grande por dentro —cambiar de lenguaje el compilador entero— podría dar miedo, pero la superficie con la que escribes queda intacta. Tus .astro de Astro 6 compilan igual en Astro 7. Esa separación entre qué acepta el compilador y cómo está implementado es justo lo que permite reescribir el motor sin pedirte reaprender nada.
Conviene detenerse en una idea que este nivel entero va a desplegar: un lenguaje no es su sintaxis, es su compilador. Podríamos escribir en una servilleta la gramática de .astro —frontmatter arriba, plantilla abajo, expresiones entre llaves— y no habríamos creado nada; sería tinta muerta hasta que un programa lea esa servilleta y decida, regla por regla, qué significa cada símbolo. El formato .astro no vive en una especificación platónica que el compilador se limita a implementar: vive dentro del compilador, y es exactamente lo que ese programa acepta, transforma y rechaza. Por eso reutilizar Babel nunca fue una opción de comodidad que Astro despreció por orgullo, sino una imposibilidad de fondo: Babel define el JavaScript que Babel entiende, tsc define su TypeScript, y ninguno tiene dentro la noción de una valla de frontmatter, de un estilo que se aísla con un hash, de una isla que se hidrata bajo una directiva. Adoptar un compilador ajeno habría sido adoptar el lenguaje de otro. Cuando Astro escribe el suyo —primero en Go, ahora en Rust— no está optimizando una tubería: está ejerciendo la única forma de soberanía que tiene un formato, que es poseer el programa que lo hace real. Y por eso el salto a Rust puede ser total por dentro sin tocar una línea de tus componentes: lo que define .astro es el contrato de entrada y salida del compilador, no el idioma en que ese contrato está implementado. Entender esto cambia cómo lees el resto del nivel. No vamos a estudiar una utilidad que acelera Astro; vamos a estudiar la pieza que, literalmente, es el lenguaje.
- Escribe un
.astromínimo con unawaiten el frontmatter y una expresión{}en la plantilla, y localiza a ojo la frontera exacta que marca la valla---. - Reescribe mentalmente ese mismo componente como si fuera JSX y anota tres cosas que dejarían de funcionar: la raíz múltiple, el
awaitarriba y el<style>como etiqueta real. - Enumera qué metadatos, además de la función de render, crees que el compilador tendría que extraer de tu archivo para que el framework sepa qué hacer con él.
- Argumenta en un párrafo por qué esos metadatos hacen imposible reutilizar
tsco Babel, aunque ambos sepan analizar JavaScript a la perfección.