Video Summary and Transcription
Esta charla aborda los puntos problemáticos y la gestión de paquetes efectiva en Monorepos, incluido el uso de diseños hoisted o aislados y los desafíos de trabajar con dependencias entre pares. Presenta la herramienta Bit, que aborda estos problemas y maneja la gestión de dependencias y el control de versiones. Bit permite la instalación y gestión automática de dependencias, admite múltiples versiones de una dependencia entre pares y actualiza los componentes de manera transparente en diferentes entornos.
1. Introducción a la Gestión de Paquetes en Monorepos
Mi nombre es Zoltan Koçan. Hablaré sobre los puntos problemáticos y compartiré recetas para una gestión efectiva de paquetes en monorepos. Me encargo de la gestión de dependencias en Bit y soy el principal mantenedor del proyecto pnpm. La historia de las herramientas de monorepo para proyectos JS comienza con Babel y Lerna. Para el año 2017, pnpm, Yarn y npm ya ofrecían soporte para monorepos. Los gestores de paquetes pueden organizar las dependencias en diseños hoisted o aislados, siendo pnpm el que utiliza el diseño aislado de módulos de nodos.
Mi nombre es Zoltan Koçan. En mi presentación, quiero hablar sobre la gestión de paquetes en monorepos. Discutiré algunos de los puntos problemáticos y compartiré recetas para una gestión efectiva de paquetes en monorepos.
Actualmente, trabajo en Bit, donde me encargo de las tareas relacionadas con la gestión de dependencias. También soy el principal mantenedor del proyecto de código abierto pnpm, que es un gestor de paquetes JS. Antes de Bit, trabajé en JustAnswer. En JustAnswer teníamos un monorepo enorme con cientos de componentes. La instalación con npm llevaba 30 minutos en ese monorepo. Esa fue la principal razón por la que empecé a contribuir a una alternativa más rápida, pnpm. Con pnpm, pudimos reducir el tiempo de instalación a unos 90 segundos.
Veamos brevemente la historia de las herramientas de monorepo para proyectos JS. Babel fue uno de los proyectos más influyentes en el ecosistema de JavaScript y probablemente fue uno de los primeros proyectos JS de código abierto populares que utilizó un monorepositorio. Los creadores de Babel crearon Lerna en 2015. Lerna podía instalar dependencias en un monorepo utilizando npm-cli bajo el capó. Dicho esto, la instalación con Lerna era terriblemente lenta, por decir lo menos. Todos sabían que los gestores de paquetes deberían implementar la instalación en monorepos de forma nativa. Para el año 2017, tanto pnpm como Yarn habían lanzado soporte para monorepos. Yarn llamó a esta característica instalación de workspaces, mientras que pnpm utilizó el término singular workspace. En un par de años, npm también lanzó soporte para workspaces. A día de hoy, hay tres gestores de paquetes maduros y populares de Node.js con soporte integrado para monorepos.
Existen dos formas en que los gestores de paquetes pueden organizar las dependencias en un monorepo: hoisted e aislado. Los tres gestores de paquetes admiten ambos diseños. Por defecto, Yarn y npm utilizan un enfoque hoisted. Con este enfoque, todas las dependencias directas e indirectas se colocan en el directorio raíz no-modules. Si hay varias versiones de las mismas dependencias, una de las versiones se anida. Como puedes ver en esta diapositiva, hay dos versiones diferentes de lodash. Por lo tanto, una de las versiones se anida en la raíz del monorepo, mientras que la otra se anida dentro de app2. pnpm utiliza un diseño diferente llamado módulos de nodos aislados. Con los módulos de nodos aislados, las dependencias de cada paquete se anidan. La ventaja de este enfoque es que los paquetes solo tienen acceso a sus propias dependencias.
2. Gestión de Dependencias en Monorepos
Mientras que con el diseño hoisted, todos los proyectos tendrían acceso al paquete cookie. Es realmente fácil desordenar las dependencias en un monorepo. Main.js en app1 está utilizando lodash que se encuentra en las dev.dependencies. Trabajar con dependencias entre pares en monorepos es un desafío. Es crucial utilizar una única versión de la dependencia entre pares en todos los paquetes del espacio de trabajo. Solo Yarn admite actualmente la sincronización de versiones de dependencias de forma nativa. pnpm tiene planes de introducir esta función a través de los catálogos del espacio de trabajo. pnpm ofrece una función para admitir múltiples versiones de una dependencia entre pares conocida como dependencias inyectadas.
Mientras que con el diseño hoisted, todos los proyectos tendrían acceso al paquete cookie, con un diseño de módulos de nodo aislado, los proyectos solo tienen acceso a sus propias dependencias. Por lo tanto, en este caso, solo app1 podrá requerir cookie.
Creo que la mayoría de las personas están de acuerdo en que los monorepos brindan una experiencia de desarrollo superior. A pesar de esto, es realmente fácil desordenar las dependencias en un monorepo. Como se puede ver en este ejemplo, la aplicación está utilizando una cookie pero no la lista en sus dependencias. Este código funcionará localmente porque la cookie se encuentra en el directorio de módulos de nodo de un directorio padre. Sin embargo, se romperá cuando alguien instale app1 fuera del monorepo.
Otro problema en este ejemplo es que main.js en app1 está utilizando lodash. Main.js es código de producción, pero lodash está listado en las dev.dependencies. Esto significa que este código funcionará localmente, pero se romperá en producción donde no se instalan las dev.dependencies. Para detectar estos dos problemas específicos, puedes utilizar una regla especial en eslint, la regla de noextraneous dependencies del complemento de importación. Si configuras esta regla de linting, eslint te notificará las dependencias que se importan pero no se declaran en package.json. En este ejemplo, recibirás un error sobre el uso de cookie en app1. eslint también te notificará que lodash es una dev.dependency. Puedes evitarlo si se utiliza en código de producción.
Trabajar con dependencias entre pares en monorepos es un desafío. Es crucial que las dependencias entre pares sean únicas durante la ejecución. Si es posible, debes intentar utilizar una única versión de la dependencia entre pares en todos los paquetes del espacio de trabajo. Como se puede ver en este ejemplo, tanto card como button hacen referencia a la misma versión de React. Esto funcionará bien. Ya sea que estés lidiando con dependencias entre pares o no, es preferible utilizar la misma versión de una dependencia en todos tus proyectos. Hacerlo puede ayudarte a evitar problemas relacionados con las dependencias entre pares y reducir el tamaño de tus paquetes. Hasta donde yo sé, solo Yarn admite actualmente la sincronización de versiones de dependencias de forma nativa utilizando restricciones. pnpm tiene planes de introducir esta función a través de los catálogos del espacio de trabajo. También es posible utilizar herramientas de terceros para encontrar duplicados de versiones. Varios herramientas de terceros actúan como linters para verificar la inconsistencia de versiones. Una de estas herramientas es Syncpack. En monorepos grandes, a veces puede resultar desafiante evitar tener múltiples versiones de una dependencia entre pares. Entre npm, yarn y pnpm, solo pnpm ofrece una función para admitir múltiples versiones de una dependencia entre pares. Esta función se conoce como dependencias inyectadas.
3. Dependencias Inyectadas e Introducción a Bit
Cuando se habilitan las dependencias inyectadas, los paquetes del espacio de trabajo se copian, lo que les permite ejecutarse con diferentes versiones del par. Después de discutir los desafíos asociados con los monorepos, me gustaría presentar otra herramienta que aborda estos problemas de manera integral. El nombre de la herramienta es Bit. Es una cadena de herramientas diseñada para construir software componible. Un espacio de trabajo de Bit se asemeja a un espacio de trabajo de pnpm, pero no hay paquetes en archivos en un espacio de trabajo de Bit. Todas las dependencias para todos los componentes se declaran en un solo manifiesto ubicado en la raíz del espacio de trabajo.
Cuando se habilitan las dependencias inyectadas, los paquetes del espacio de trabajo se copian, lo que les permite ejecutarse con diferentes versiones del par. Como se puede ver en este ejemplo, el componente Button utiliza la versión 17 de React, pero el componente Card utiliza la versión 16 de React. Cuando ejecutamos pruebas para el componente Button, queremos utilizar la versión 17 de React. En cambio, cuando ejecutamos el componente Button desde dentro del componente Card, queremos que Button utilice la misma versión de React que Card. Por lo tanto, se instala la versión 17 de React en el directorio node_modules de Button. Mientras tanto, Card no hace referencia directamente a Button desde el espacio de trabajo, sino desde un subdirectorio oculto donde se alinea con la versión 16 de React. Esta configuración significa que hay dos instancias del componente Button en el espacio de trabajo, una con la versión 17 de React y otra, una copia dentro del directorio node_modules.pnpm con la versión 16 de React. La única desventaja de este enfoque es que una vez que se modifica el componente Button, es necesario volver a ejecutar el proceso de instalación para que pnpm pueda actualizar los archivos con el componente Button en las instancias copiadas.
Después de discutir los desafíos asociados con los monorepos, me gustaría presentar otra herramienta que aborda estos problemas de manera integral. El nombre de la herramienta es Bit. Si bien Bit no es un gestor de paquetes, la gestión de paquetes es una de sus principales responsabilidades. Entonces, ¿qué es exactamente Bit? Es una cadena de herramientas diseñada para construir software componible. Puedes conceptualizarlo como una alternativa a Git, GitHub, el registro de npm y varios clientes de npm. Al utilizar Bit, sirve como tu sistema de control de versiones, administra tus dependencias y publica tus paquetes. Para esta presentación, nos centraremos únicamente en la gestión de paquetes, por lo que solo discutiré el aspecto de instalación de Bit.
De muchas maneras, un espacio de trabajo de Bit se asemeja a un espacio de trabajo de pnpm. Es una colección de paquetes o componentes. Sin embargo, hay una diferencia distintiva, ya que no hay paquetes en archivos en un espacio de trabajo de Bit. En su lugar, todas las dependencias para todos los componentes se declaran en un solo manifiesto ubicado en la raíz del espacio de trabajo. Además, no hay un campo separado para las dependencias de desarrollo. Este enfoque simplificado es factible porque Bit realiza un análisis de código de los componentes dentro del espacio de trabajo. Identifica automáticamente qué componentes utilizan qué dependencias y distingue si una dependencia en particular es para producción o desarrollo para un componente dado. Permíteme mostrarte ahora una demostración rápida de un espacio de trabajo de Bit. Para la demostración, utilizaré VS Code con la extensión de Bit instalada. Como puedes ver, ya lo tengo instalado en mi computadora. Vamos a la sección de Bit y comencemos un nuevo espacio de trabajo. Bit ha creado un manifiesto de espacio de trabajo para mí. Ahora generemos algunos componentes nuevos utilizando la herramienta de línea de comandos de Bit. Crearé dos nuevas aplicaciones de nodo, app1 y app2. Aquí están los directorios de mis componentes de aplicaciones recién creados.
4. Gestión de Dependencias con Bit
Como puedes ver, ni app1 ni app2 contienen paquetes en archivos. Bit ejecuta pnpm install para instalar las dependencias. Añadamos nuevas declaraciones de importación para lodash y ramda. Bit añadirá automáticamente las nuevas dependencias al espacio de trabajo. Después de la instalación, las nuevas dependencias son visibles en los detalles del componente. Bit también maneja el control de versiones. Los cambios en las dependencias pueden ser gestionados fácilmente. Bit detecta automáticamente los cambios en los tipos de dependencia.
Como puedes ver, ni app1 ni app2 contienen paquetes en archivos. Bit crea dinámicamente los paquetes en archivos al publicar. En el fondo, Bit ejecuta pnpm install para instalar cualquier dependencia. Como puedes ver, aquí está la salida de pnpm. Ahora instala algunas dependencias que están presentes de forma predeterminada en los componentes de la aplicación de nodo.
Volvamos al menú de Bit. Aquí podemos ver la lista de nuestros componentes. Como puedes ver en los detalles del componente, las dependencias del componente. Añadamos algunas nuevas declaraciones de importación. Añadiré lodash a app1 y ramda a app2. Como puedes ver, estas dependencias actualmente faltan. En un espacio de trabajo de pnpm, ejecutarías algo como pnpm add para la dependencia de app1 lodash y add para la dependencia de app2 ramda. Ahora con Bit, es más fácil. Solo necesitas ejecutar bit install add missing deps y Bit realizará un análisis de código, encontrará cualquier nueva declaración de importación con dependencias que aún no estén instaladas en el espacio de trabajo y automáticamente añadirá estas nuevas dependencias al espacio de trabajo.
Una vez que la instalación finalice, debemos poder ver las nuevas dependencias en los detalles del componente. Así que sí, aquí está. Ahora lodash está en las dependencias de app1 y ramda está en las dependencias de app2. También podemos ver que estos nuevos paquetes aparecieron en el manifiesto del espacio de trabajo aquí y se instalaron en node_modules ramda lodash. Ahora hagamos commit a nuestros cambios. Usaré Bit para hacer el commit de los cambios porque Bit también es un sistema de control de versiones. También puedes usar Bit con git, no hay problema. Ahora hagamos algunos cambios en las dependencias nuevamente. Así que ahora eliminaré lodash de app1 TS y lo moveré a appspec TS. Ahora se convierte en una dependencia de desarrollo. Y también eliminemos completamente ramda de app2. Ahora ejecutemos la instalación. En un espacio de trabajo de pnpm, después de hacer estos cambios, tendrías que actualizar manualmente los paquetes en app1. Tendrías que eliminar lodash de la sección de dependencias y ponerlo en la sección de dependencias de desarrollo. Con Bit esto no es necesario. Detecta automáticamente que el tipo de la dependencia ha cambiado.
5. Gestión Avanzada de Dependencias con Bit
Con Bit, no es necesario eliminar manualmente las dependencias. Bit maneja automáticamente los cambios en los tipos de dependencia. Bit también facilita la compilación de paquetes, permitiendo el soporte de múltiples versiones de una dependencia entre pares. Los componentes se cargan desde entornos separados según sus dependencias. Bit actualiza los componentes de manera transparente en todos los entornos.
Y también con pnpm tendrías que eliminar manualmente ramda de las dependencias de app2. Pero con Bit esto no es necesario. Como puedes ver aquí en los detalles del componente, ramda no está en las dependencias de app2. Y en app1, lodash ahora es una dependencia de desarrollo, no una dependencia de producción. Esto sucedió automáticamente bajo el capó. No se requirió ninguna acción por parte del usuario. Si revisamos los cambios, también podemos ver cualquier cambio en las dependencias. Y podemos ver que en app1, lodash cambió de una dependencia de tiempo de ejecución o producción a una dependencia de desarrollo. Y en app2, ramda se eliminó por completo.
Además de gestionar las dependencias, Bit también maneja la compilación de paquetes dentro del espacio de trabajo. Esto facilita considerablemente el soporte de múltiples versiones de una dependencia entre pares en comparación con pnpm. Como se ilustra en esta diapositiva, nuestro espacio de trabajo contiene cuatro componentes. Dos páginas, un botón y una tarjeta. Queremos usar dos versiones de React en este espacio de trabajo. Entonces Bit crea dos entornos de ejecución separados para los componentes. Uno con React v16 y otro con React v17. Los componentes que dependen de React v16 se cargan desde el entorno correspondiente junto con todas sus dependencias. De manera similar, los componentes que dependen de React v17 se cargarán desde el otro entorno. Todas sus dependencias utilizarán React v17. Si hay algún cambio en un componente, Bit actualizará ese componente de manera transparente en todos los entornos.
Eso es todo lo que quería compartir con ustedes hoy. Gracias a todos. Adiós.
Comments