• parcel
  • vite
  • js
  • esbuild
  • bundler
  • monorepo

De Parcel a Vite: Una breve historia de una migración de 100K LOC

Hemos migrado nuestros tres proyectos frontend de Parcel a Vite, y el proceso fue... fluido.

Gao
Gao
Founder

La historia de fondo

Tenemos tres proyectos frontend principales en Logto: la experiencia de inicio de sesión, la Consola y la vista previa en vivo. Estos proyectos están todos en TypeScript, React, y módulos SASS; en total, tienen alrededor de 100K líneas de código.

Nos encantaba Parcel por su simplicidad y configuración sin necesidad de ajustes. Todavía puedo recordar el día en que me asombré de lo fácil que fue configurar un nuevo proyecto con Parcel. Puedes simplemente ejecutar parcel index.html y boom, todas las dependencias necesarias están instaladas y el proyecto está en marcha. Si eres un desarrollador "experimentado", quizá te sientas de la misma manera comparándolo con los viejos tiempos de configuración con Gulp y Webpack. Parcel es como una varita mágica.

La simplicidad de Parcel fue la razón principal por la que nos quedamos con él tanto tiempo, aunque a veces podía ser caprichoso. Por ejemplo:

  • A veces, Parcel no pudo empaquetar el proyecto porque no pudo encontrar algunos archivos de fragmentos que en realidad estaban allí.
  • Necesitaba algunas configuraciones hackeadas para trabajar con nuestra configuración de monorepo.
  • No soporta MDX 3 de forma nativa, así que tuvimos que crear un transformador personalizado para ello.
  • No admite fragmentos manuales (al momento de escribir esto, la función de fragmentos manuales todavía está en la etapa experimental), lo cual está bien para la mayoría de las circunstancias, pero a veces lo necesitas.

Entonces, ¿por qué decidimos migrar a otra cosa?

  1. Nos quedamos atascados con Parcel 2.9.3, que fue lanzado en junio de 2023. Cada vez que se lanzaba una nueva versión después de eso, intentábamos actualizar, pero siempre fallaba con errores de compilación.
  2. La versión más reciente de Parcel era 2.12.0, lanzada en febrero de 2024. Aunque tiene commits casi diarios, no se ha realizado ningún lanzamiento nuevo desde entonces.

Alguien incluso abrió una discusión para preguntar si Parcel está muerto. La respuesta oficial es que no, Parcel sigue vivo, pero está en un estado de "estamos trabajando en una gran refactorización y no tenemos tiempo para lanzamientos menores". Para nosotros, es como una "muerte de pato": La versión más reciente que podemos usar es de hace más de un año, y no sabemos cuándo se lanzará la próxima versión. Parece que está muerto, actúa como si estuviera muerto, así que para nosotros está muerto.

Solicitudes de actualización de Parcel

Confía en mí, lo hemos intentado.

¿Por qué Vite?

Conocíamos Vite por Vitest. Hace varios meses, estábamos cansados del soporte de ESM de Jest (en las pruebas) y queríamos probar algo nuevo. Vitest nos ganó el corazón con el soporte nativo de ESM y la compatibilidad con Jest. Tiene una experiencia de desarrollador impresionante, y está impulsado por Vite.

El estado actual

Puedes tener configuraciones diferentes en tu proyecto, pero generalmente encontrarás reemplazos de plugins a medida que el ecosistema de Vite florece. Estas son nuestras configuraciones al momento de la migración:

  • Monorepo: Usamos workspaces de PNPM (v9) para gestionar nuestro monorepo.
  • Módulo: Usamos módulos ESM para todos nuestros proyectos.
  • TypeScript: Usamos TypeScript (v5.5.3) para todos nuestros proyectos con alias de rutas.
  • React: Usamos React (v18.3.1) para todos nuestros proyectos frontend.
  • Estilos: Usamos módulos SASS para estilización.
  • SVG: Usamos SVGs como componentes de React.
  • MDX: Tenemos MDX con Markdown con Sabor GitHub y soporte para Mermaid.
  • Carga diferida: Necesitamos cargar algunas de nuestras páginas y componentes de forma diferida.
  • Compresión: Producimos activos comprimidos (gzip y brotli) para nuestras compilaciones de producción.

La migración

Comenzamos la migración creando un nuevo proyecto de Vite y experimentando con él para ver cómo funcionaba. El proceso fue fluido y la migración real solo tomó unos pocos días.

Soporte listo para usar

Vite tiene soporte listo para usar para monorepo, ESM, TypeScript, React, y SASS. Solo necesitábamos instalar los plugins y configuraciones necesarias para hacerlo funcionar.

Alias de ruta

Vite tiene soporte incorporado para alias de ruta, por ejemplo, en nuestro tsconfig.json:

Solo necesitábamos agregar la misma resolución en nuestro vite.config.ts:

Ten en cuenta que la ruta de reemplazo debe ser una ruta absoluta, aunque es relativa a la raíz del proyecto. Alternativamente, puedes usar el plugin vite-tsconfig-paths para leer los alias de rutas desde el tsconfig.json.

React Fast Refresh y HMR

Aunque Vite tiene soporte incorporado para HMR, es necesario instalar un plugin para habilitar React Fast Refresh. Usamos el plugin @vitejs/plugin-react que es proporcionado por el equipo de Vite y tiene un gran soporte para características de React como Fast Refresh:

SVG como componente React

Usamos el plugin vite-plugin-svgr para convertir SVGs en componentes de React. Es tan simple como agregar el plugin a la configuración de Vite:

Sin embargo, no especificamos bajo qué condición los SVGs deberían ser convertidos a componentes React, por lo que todas las importaciones fueron convertidas. El plugin ofrece una mejor configuración por defecto: solo convierte los SVGs que se importan con la extensión .svg?react. Actualizamos nuestras importaciones en consecuencia.

Módulos SASS

Aunque Vite tiene soporte incorporado para módulos SASS, hay algo de lo que debemos ocuparnos: cómo se formatean los nombres de clase. Puede ser problemático para los usuarios y para nuestras pruebas de integración si los nombres de las clases no están formateados consistentemente. La configuración de una sola línea en el vite.config.ts puede resolver el problema:

Por cierto, Parcel y Vite tienen diferentes formas de importar archivos SASS:

La sintaxis * as, sin embargo, funciona en Vite, pero causará la pérdida de nombres de clase modularizados cuando usas claves dinámicas para acceder al objeto styles. Por ejemplo:

Soporte para MDX

Dado que Vite aprovecha Rollup por debajo, podemos usar el plugin oficial @mdx-js/rollup para soportar MDX junto con sus plugins. La configuración se ve así:

El plugin remarkGfm se utiliza para soportar Markdown con Sabor GitHub, y el plugin rehypeMdxCodeProps se utiliza para pasar las props a los bloques de código en los archivos MDX como lo hace Docusaurus.

Soporte para Mermaid dentro de MDX

Nos gustaría usar diagramas Mermaid en nuestros archivos MDX como en otros lenguajes de programación. El uso debe ser tan simple como otros bloques de código:

Debe renderizarse como:

Dado que nuestra aplicación soporta temas claros y oscuros, codificamos un poco para hacer que los diagramas Mermaid funcionen con el tema oscuro. Se crea un componente React:

useTheme es un hook personalizado para obtener el tema actual del contexto. La librería mermaid se importa de forma asíncrona para reducir el tamaño de carga durante la carga inicial de la página.

Para el bloque de código en el archivo MDX, tenemos un componente unificado para hacer el trabajo:

Finalmente, definimos el proveedor MDX de la siguiente manera:

Carga diferida

Esto no es algo específico de Vite, sigue siendo digno de mención ya que actualizamos nuestras páginas para usar la carga diferida durante la migración, y nada se rompió después de eso.

React tiene una función incorporada React.lazy para cargar componentes de forma diferida. Sin embargo, puede causar algunos problemas cuando estás iterando rápidamente. Creamos una pequeña biblioteca llamada react-safe-lazy para resolver los problemas. Es un reemplazo directo de React.lazy y una explicación detallada se puede encontrar en esta publicación en el blog.

Compresión

Hay un plugin ordenado llamado vite-plugin-compression para producir activos comprimidos. Soporta tanto la compresión gzip como brotli. La configuración es simple:

Fragmentos manuales

Una gran característica de Vite (o del Rollup subyacente) es la de los fragmentos manuales. Mientras que React.lazy se usa para la carga diferida de componentes, podemos tener más control sobre los fragmentos especificando los fragmentos manuales para decidir qué componentes o módulos deben agruparse.

Por ejemplo, podemos usar primero vite-bundle-visualizer para analizar el tamaño del paquete y las dependencias. Luego podemos escribir una función adecuada para dividir los fragmentos:

Servidor de desarrollo

A diferencia de la compilación de producción, Vite NO agrupará tu código fuente en el modo de desarrollo (incluidas las dependencias vinculadas en el mismo monorepo) y tratará cada módulo como un archivo. Para nosotros, el navegador cargará cientos de módulos por primera vez, lo que parece una locura, pero en realidad está bien en la mayoría de los casos. Puedes ver la discusión aquí.

Si esto es un problema para ti, una solución alternativa (pero no perfecta) es listar las dependencias vinculadas en la opción optimizeDeps del vite.config.ts:

Esto "pre-agrupará" las dependencias vinculadas y hará que el servidor de desarrollo sea más rápido. El inconveniente es que HMR puede no funcionar como se espera para las dependencias vinculadas.

Además, usamos un proxy que sirve los archivos estáticos en producción y retransmite las solicitudes al servidor de Vitest en desarrollo. Tenemos algunos puertos específicos configurados para evitar conflictos, y también es fácil de configurar en el vite.config.ts:

Variables de entorno

A diferencia de Parcel, Vite usa un enfoque moderno para manejar las variables de entorno utilizando import.meta.env. Cargará automáticamente los archivos .env y reemplazará las variables en el código. Sin embargo, requiere que todas las variables de entorno se prefijen con VITE_ (configurable).

Mientras usábamos Parcel, simplemente reemplazaba las variables process.env sin comprobar el prefijo. Así que encontramos una solución usando el campo define para facilitar la migración:

Esto nos permite agregar gradualmente el prefijo a las variables de entorno y eliminar el campo define.

Conclusión

¡Eso es todo! Hemos migrado con éxito nuestros tres proyectos frontend de Parcel a Vite, y esperamos que esta breve historia pueda ayudarte con tu migración. Así es como luce la configuración al final: