Configurar Vite desde astro.config: la clave vite
La clave vite de astro.config es la puerta directa a la configuración del motor subyacente. Cómo usar resolve.alias para nombrar rutas de importación, el binomio ssr.external y ssr.noExternal para decidir qué dependencias se empaquetan en el servidor y cuáles quedan fuera, optimizeDeps para afinar el pre-empaquetado en desarrollo, y plugins para extender la tubería cuando la configuración de alto nivel de Astro no llega.
Astro te ofrece abstracciones cómodas para el noventa por ciento de los casos, pero no finge cubrirlo todo. Para el diez por ciento restante existe una única llave que abre el motor entero: la clave vite de astro.config. Todo lo que aceptaría un fichero de configuración de Vite cabe ahí dentro, fusionado con lo que Astro ya configura por ti. Dominar esa clave es saber cuándo puedes resolver un problema desde arriba, con una integración pulida, y cuándo tienes que bajar a la maquinaria y girar un tornillo concreto.
- Entender la clave
vitecomo válvula de escape que se fusiona con la configuración interna de Astro. - Nombrar rutas de importación con
resolve.aliasen lugar de cadenas relativas frágiles. - Decidir qué dependencias se empaquetan en el servidor con
ssr.externalyssr.noExternal. - Afinar el pre-empaquetado con
optimizeDepsy extender la tubería conplugins.
La clave vite como válvula de escape
Cuando escribes una clave vite en astro.config, no reemplazas la configuración de Vite: la fusionas con la que Astro ya genera. Astro configura por debajo su plugin del compilador, los ajustes de SSR según tu adapter y decenas de detalles que no deberías tocar. Tu objeto vite se mezcla encima, añadiendo o ajustando lo que necesites sin desmontar lo anterior. Por eso conviene tratarla con respeto: es potente porque es directa, y directa significa sin red.
import { defineConfig } from 'astro/config';
export default defineConfig({
vite: {
resolve: {
alias: { '@lib': '/src/lib', '@ui': '/src/components/ui' },
},
ssr: {
noExternal: ['paquete-solo-esm'],
external: ['dependencia-con-binario-nativo'],
},
optimizeDeps: {
include: ['libreria-cjs-profunda'],
},
plugins: [],
},
});
La regla de oro es no usar la clave vite para algo que Astro ya ofrece de forma tipada. El resaltado de código vive en markdown, las capacidades enchufables en integrations, el modo de render en output y adapter. Bajar a vite para eso es saltarse la abstracción estable y atarte a detalles internos. Reserva la clave para lo que de verdad no tiene una puerta de alto nivel: un plugin ajeno, un alias, un ajuste de SSR.
resolve.alias: nombrar rutas
La primera necesidad honesta que suele empujarte a vite es dejar de escribir importaciones relativas frágiles. Un import que sube cuatro carpetas con puntos y barras es ilegible y se rompe en cuanto mueves el fichero. Con resolve.alias das un nombre estable a una carpeta y el motor lo resuelve por ti en todas partes.
export default defineConfig({
vite: {
resolve: {
alias: {
'@components': '/src/components',
'@lib': '/src/lib',
'@styles': '/src/styles',
},
},
},
});
Con esto, import Boton from '@components/Boton.astro' funciona desde cualquier profundidad del proyecto. Un matiz importante: el alias lo resuelve Vite en tiempo de build, pero tu editor y el chequeo de tipos leen tsconfig.json. Para que ambos coincidan conviene declarar los mismos nombres en la clave paths de tsconfig.json.
// tsconfig.json: los mismos alias, para que el editor los entienda
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "@components/*": ["src/components/*"], "@lib/*": ["src/lib/*"] }
}
}
La regla es mantener las dos listas sincronizadas: Vite resuelve el build, tsconfig alimenta al editor. Cuando divergen aparece el desconcierto clásico de un import que compila pero el editor subraya en rojo, o al revés, un import que el editor acepta y el build no encuentra.
ssr.external y noExternal: qué se empaqueta para el servidor
Aquí vive el ajuste más sutil y más útil de todos. Cuando construyes para SSR, Vite debe decidir, dependencia a dependencia, si la empaqueta dentro del bundle del servidor o la deja externa —es decir, la importa en tiempo de ejecución desde node_modules como haría Node—. Por defecto, Vite externaliza las dependencias de Node: no las empaqueta, confía en que estén instaladas en el servidor. Esa política acierta casi siempre, pero falla en dos casos opuestos, y para cada uno hay una clave.
flowchart TD DEP[dependencia en el build de servidor] --> DEF[por defecto se externaliza] DEF --> EXT[queda como import en tiempo de ejecucion] DEP --> NOEXT[si la pones en ssr noExternal] NOEXT --> BUND[se empaqueta dentro del bundle del servidor] DEP --> FORCE[si la pones en ssr external] FORCE --> EXT style BUND fill:#a6e3a1,color:#11111b style EXT fill:#89b4fa,color:#11111b
ssr.noExternal
Fuerza a empaquetar una dependencia dentro del servidor. Se usa cuando la librería necesita transformarse, publica solo ESM o trae assets que el runtime destino no resolvería suelta.
ssr.external
Fuerza a dejar una dependencia fuera del bundle. Se usa para paquetes con binarios nativos o que deben cargarse tal cual en tiempo de ejecución, sin pasar por el bundler.
El caso típico de noExternal es una librería que asume que alguien la va a empaquetar y no funciona si se importa cruda en el servidor —frecuente en paquetes de estilos o de componentes—. El caso típico de external es un módulo con un binario nativo compilado que el bundler no puede ni debe procesar. Cuando un despliegue de SSR falla con un error de módulo que no se encuentra o de sintaxis inesperada al arrancar el servidor, la causa suele estar en esta frontera, y la cura es mover el paquete conflictivo a la lista correcta.
Una advertencia sobre el runtime de destino: qué debe externalizarse depende de dónde se ejecute el servidor. Un adapter de Node puede dejar externas las dependencias porque estarán instaladas junto al servidor; un adapter de edge, sin node_modules en tiempo de ejecución, obliga a empaquetar mucho más dentro del propio bundle. El mismo proyecto puede necesitar listas distintas según a dónde lo despliegues, y por eso conviene tratar estas claves como parte de la configuración de despliegue, no como un ajuste universal.
optimizeDeps y plugins: pre-empaquetado y extensión
optimizeDeps gobierna el pre-empaquetado de dependencias del dev server, esa fase previa que convierte a ESM y colapsa los ficheros de cada librería. Casi siempre acierta sola, pero tiene dos escotillas. Con optimizeDeps.include fuerzas a pre-empaquetar una dependencia que Vite no detectó —típico cuando una librería esconde sus imports tras CommonJS profundo—. Con optimizeDeps.exclude la excluyes, útil para un paquete que debe servirse sin tocar.
export default defineConfig({
vite: {
optimizeDeps: {
include: ['libreria-con-imports-cjs-ocultos'],
exclude: ['paquete-que-sirve-su-propio-esm'],
},
},
});
La clave plugins cierra el círculo: es donde enchufas plugins de Vite o de la API de Rollup que Rolldown mantiene compatible. Antes de escribir uno, recuerda que Astro extiende sus capacidades por integrations, no por plugins sueltos; baja a vite.plugins solo cuando necesitas un plugin del ecosistema que no viene envuelto en una integración de Astro.
Los usos legítimos de vite.plugins en un proyecto Astro suelen ser contados y reconocibles:
- Un visualizador de bundle para auditar tamaños, que verás en la lección de diagnóstico.
- Un plugin que transforma un formato de fichero que Astro no maneja de fábrica.
- Un plugin de inspección o depuración que quieres activo solo durante el desarrollo.
Si tu sitio funciona en astro dev pero el build de servidor peta al desplegar con un error extraño sobre un módulo, antes de reescribir nada prueba a mover la dependencia sospechosa a ssr.noExternal. Muchísimos fallos de SSR se reducen a una librería que se externalizó cuando debía empaquetarse. Es el primer tornillo que hay que girar, no el último.
La existencia misma de la clave vite dice algo profundo sobre cómo Astro entiende su propio papel, y vale la pena leerlo despacio porque es una decisión de diseño que muchos frameworks no se atreven a tomar. Toda abstracción hace una apuesta: cubrir los casos comunes con una interfaz limpia y esconder la complejidad de debajo. El problema es que ninguna abstracción cubre el cien por cien de la realidad, y cuando un framework finge que sí, condena a sus usuarios a un callejón el día que necesitan algo que la interfaz no previó. Ese día, o el framework te da una salida honesta o te quedas atrapado reescribiendo tu proyecto para esquivar la limitación. Astro elige la salida honesta: output, integrations y markdown son las abstracciones pulidas para lo común, y vite es la puerta deliberada que reconoce, sin vergüenza, que debajo hay una máquina que a veces tendrás que tocar directamente. Esa gradación no es dejadez, sino madurez: pasas de lo declarativo y estable —donde Astro te protege— a lo potente y frágil —donde Astro se aparta y te deja el mando—, y el propio diseño te avisa de en qué zona estás por lo específica y de bajo nivel que se vuelve la clave. Aprender a leer esa frontera es una de las habilidades más transferibles que existen: te dice cuándo estás dentro del camino trazado, disfrutando de garantías y migraciones cuidadas, y cuándo te has salido a territorio que ahora mantienes tú, donde una actualización del motor podría pedirte un ajuste. Un buen framework no es el que oculta la máquina hasta que no puedes alcanzarla; es el que la organiza en capas y te entrega la llave de las de abajo cuando de verdad la necesitas, confiando en que sabrás cuándo usarla y cuándo no. La clave vite es esa llave, y saber que existe —y la disciplina de no abusar de ella— es parte de dominar Astro tanto como conocer sus abstracciones de alto nivel.
- Añade un
resolve.aliaspara tu carpeta de componentes y refactoriza una importación relativa larga para que use el alias; declara el mismo nombre entsconfig.jsony confirma que el editor no protesta. - Provoca a propósito un fallo de SSR importando en una ruta de servidor una librería que no se empaquete bien, y resuélvelo moviéndola a
ssr.noExternal. - Fuerza el pre-empaquetado de una dependencia con
optimizeDeps.includey observa en el arranque deastro devcómo aparece en la fase de optimización. - Razona, para tres dependencias reales de tu proyecto, cuál debería ir en
noExternal, cuál enexternaly cuál dejar en la política por defecto, y justifica cada decisión.