wandres.dev
VITE 8 Y ROLLDOWN · el motor por debajo

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.

⏱ 17 min

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.

🎯 Al terminar esta lección sabrás
  • Entender la clave vite como válvula de escape que se fusiona con la configuración interna de Astro.
  • Nombrar rutas de importación con resolve.alias en lugar de cadenas relativas frágiles.
  • Decidir qué dependencias se empaquetan en el servidor con ssr.external y ssr.noExternal.
  • Afinar el pre-empaquetado con optimizeDeps y extender la tubería con plugins.

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: [],
  },
});
📝
Antes de bajar a vite, mira si Astro ya lo expone

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.
💡
Un error de SSR que solo aparece en producción casi siempre es esto

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.

Una válvula de escape es una promesa sobre los límites de la abstracción

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.

⚔️ Gira los tornillos del motor
  1. Añade un resolve.alias para tu carpeta de componentes y refactoriza una importación relativa larga para que use el alias; declara el mismo nombre en tsconfig.json y confirma que el editor no protesta.
  2. 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.
  3. Fuerza el pre-empaquetado de una dependencia con optimizeDeps.include y observa en el arranque de astro dev cómo aparece en la fase de optimización.
  4. Razona, para tres dependencias reales de tu proyecto, cuál debería ir en noExternal, cuál en external y cuál dejar en la política por defecto, y justifica cada decisión.