paint-brush
Cómo crear un Monorepo con Vite, Cloudflare, Remix, PNPM y Turborepo (sin paso de compilación)por@josejaviasilis
1,164 lecturas
1,164 lecturas

Cómo crear un Monorepo con Vite, Cloudflare, Remix, PNPM y Turborepo (sin paso de compilación)

por Jose Javi Asilis19m2024/08/28
Read on Terminal Reader

Demasiado Largo; Para Leer

Esto crea un repositorio único para las implementaciones de Cloduflare de Vite de Remix sin un paso de compilación. Esto le permite extenderlo a múltiples destinos.
featured image - Cómo crear un Monorepo con Vite, Cloudflare, Remix, PNPM y Turborepo (sin paso de compilación)
Jose Javi Asilis HackerNoon profile picture
0-item




Introducción

Necesitaba una forma de usar Remix con Vite y Cloudflare Workers-Pages con una configuración mínima.


Vi otros repositorios, como:


Pero tenían algunas limitaciones:

  1. No quería compilarlo previamente, ya que no quería envenenar los repositorios con más archivos de configuración.


  2. Cloudflare Workers/Pages tiene un objetivo diferente. Se volvió complicado apuntarlo con tsup, ya que paquetes como Postgres extraían las dependencias de los nodos y se rompían al importarlos a Remix.


  3. También necesitaba una forma de consumir diferentes objetivos (Remix-Cloudflare, Node/Bun)


¡Aun así, les agradezco porque allanaron el camino para que esto fuera posible!


¡Asegúrate de leer la sección de peligros al final!

¡Sígueme en las redes sociales!

Estoy construyendo una plataforma de pruebas automatizadas en público para detectar esos errores del 1% en producción.


Comparto mis avances en:


X/Twitter @javiasilis

LinkedIn @javiasilis

Repositorio de GitHub

Puedes acceder a la implementación completa aquí .

Paso a paso

Requisitos

  1. NodeJS
  2. Ministerio de Salud Pública
  3. Docker (opcional: para el ejemplo de base de datos local)


Si bien esto lo guía a través de un nuevo mono-repositorio, es perfectamente válido transformar uno existente en uno.


Esto también supondrá que tienes algún conocimiento sobre repositorios mono.


Nota:

  • “En la raíz” se refiere a la ruta inicial de su monorepositorio. Para este proyecto, estará fuera de los directorios libs y packages .

Instalar Turborepo

Turborepo funciona sobre los espacios de trabajo de tu administrador de paquetes para administrar los scripts y los resultados de tu proyecto (incluso puede almacenar en caché los resultados). Hasta el momento, es la única herramienta mono-repositorio además de Rush (que no he probado y no me gusta) que es capaz de funcionar.


NX no tiene soporte para Vite de Remix (al momento de escribir este artículo, 28 de agosto de 2024).


https://turbo.build/

 pnpm dlx create-turbo@latest

1. Configurar espacios de trabajo de PNPM

Utilizaremos las capacidades del espacio de trabajo de PNPM para administrar las dependencias.


En su directorio Monorepo, cree un pnpm-workspace.yaml .


Dentro de ella, añade:

 packages: - "apps/*" - "libs/*"


Esto le indicará a pnpm que todos los repositorios se encontrarán dentro de apps y libs . Tenga en cuenta que no importa si se utilizan libs o packages (como puede haber visto en otro lugar).

2. Cree un package.json vacío en la raíz del proyecto:

 pnpm init
 { "name": "@repo/main", "version": "1.0.0", "scripts": {}, "keywords": [], "author": "", "license": "ISC" }


Tenga en cuenta el name:@repo/main Esto nos indica que esta es la entrada principal de la aplicación. No es necesario seguir una convención en particular ni utilizar el prefijo @ . La gente lo utiliza para diferenciarlo de los paquetes locales o remotos o para facilitar su agrupación en una organización.

3. Cree el archivo turbo.json en la raíz del proyecto:

 { "$schema": "https://turbo.build/schema.json", "tasks": { "build": {}, "dev": { "cache": false, "persistent": true }, "start": { "dependsOn": ["^build"], "persistent": true }, "preview": { "cache": false, "persistent": true }, "db:migrate": {} } }


El archivo turbo.json le indica al repositorio de Turbo cómo interpretar nuestros comandos. Todo lo que se encuentre dentro de la clave tasks coincidirá con lo que se encuentre en el archivo package.json.


Tenga en cuenta que definimos cuatro comandos. Estos coinciden con los que se encuentran en la sección de script del package.json de cada repositorio. Sin embargo, no todos los package.json deben implementar estos comandos.


Por ejemplo: el comando dev se activará con turbo dev y ejecutará todos los paquetes que dev encuentren en package.json. Si no lo incluye en turbo, no se ejecutará.

4. Crea una carpeta apps en la raíz del proyecto

 mkdir apps

5. Crea una aplicación Remix en la carpeta apps (o mueve una existente)

 npx create-remix --template edmundhung/remix-worker-template


Cuando le pida que Install any dependencies with npm responda no.


  • Así es como debería verse

6. Cambie el name del paquete.json a @repo/my-remix-cloudflare-app (o su nombre)

 { - "name": "my-remix-cloudflare-app", + "name": "@repo/my-remix-cloudflare-app", "version": "1.0.0", "keywords": [], "author": "", "license": "ISC" }

7. Copie las dependencias y devDependencies desde apps/<app>/package.json al package.json de la raíz

P.ej:

<root>/paquete.json

 { "name": "@repo/main", "version": "1.0.0", "scripts": {}, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@markdoc/markdoc": "^0.4.0", "@remix-run/cloudflare": "^2.8.1", "@remix-run/cloudflare-pages": "^2.8.1", "@remix-run/react": "^2.8.1", "isbot": "^3.6.5", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@cloudflare/workers-types": "^4.20240222.0", "@octokit/types": "^12.6.0", "@playwright/test": "^1.42.1", "@remix-run/dev": "^2.8.1", "@remix-run/eslint-config": "^2.8.1", "@tailwindcss/typography": "^0.5.10", "@types/react": "^18.2.64", "@types/react-dom": "^18.2.21", "autoprefixer": "^10.4.18", "concurrently": "^8.2.2", "cross-env": "^7.0.3", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "husky": "^9.0.11", "lint-staged": "^15.2.2", "msw": "^2.2.3", "postcss": "^8.4.35", "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.12", "rimraf": "^5.0.5", "tailwindcss": "^3.4.1", "typescript": "^5.4.2", "vite": "^5.1.5", "vite-tsconfig-paths": "^4.3.1", "wrangler": "^3.32.0" } }


8. Agregue Turbo como una dependencia de desarrollo en el nivel raíz (esto instalará todos los paquetes de Remix).

Verifique que Turbo esté dentro de devDependencies de package.json. Si no está en la lista, ejecute el siguiente comando:

 pnpm add turbo -D -w


El indicador -w le indica a pnpm que lo instale en la raíz del espacio de trabajo.

9. Agregue las siguientes entradas al paquete raíz package.json

  • Agregar el comando dev a scripts

  • Agregue el packageManager a la opción


 { "name": "@repo/main", "version": "1.0.0", "scripts": { "dev": "turbo dev" }, "keywords": [], "author": "", "license": "ISC", "packageManager": "[email protected]", "dependencies": { // omitted for brevity }, "devDependencies": { // omitted for brevity } }

10. Verifique que todo esté funcionando ejecutando pnpm dev

 pnpm dev

11. Cree una carpeta Libs en la raíz del proyecto. Agregue config, db y utils:

 mkdir -p libs/config libs/db libs/utils

12. Agregue un src/index.ts para cada uno de los paquetes.

 touch libs/config/src/index.ts libs/db/src/index.ts libs/utils/src/index.ts
  • El archivo index.ts se utilizará para exportar todos los paquetes.
  • Usaremos la carpeta como nuestro punto de entrada para hacer todo compacto.
  • Esta es una convención y puedes elegir no seguirla.

13. Cree un package.json vacío y agregue lo siguiente al archivo libs/config/package.json :

 { "name": "@repo/config", "version": "1.0.0", "type": "module", "exports": { ".": { "import": "./src/index.ts", "default": "./src/index.ts" } } }

14. Haga lo mismo para libs/db/package.json :

 { "name": "@repo/db", "version": "1.0.0", "exports": { ".": { "import": "./src/index.ts", "default": "./src/index.ts" } } }

15. Y libs/utils/package.json :

 { "name": "@repo/utils", "version": "1.0.0", "exports": { ".": { "import": "./src/index.ts", "default": "./src/index.ts" } } }


  • Especificamos el campo “exportaciones”. Esto indica a otros repositorios desde dónde importar el paquete.
  • Especificamos el campo “nombre”. Este se utiliza para instalar el paquete y referirlo a otros repositorios.

16. (DB) - Agregar Drizzle y Postgres

Notas:

  • Estoy empezando a despreciar los ORM. He pasado más de 10 años aprendiendo 6 diferentes y es un conocimiento que no se puede transferir.

  • Tienes problemas cuando aparecen nuevas tecnologías. Prisma no es compatible con los trabajadores de Cloudflare de manera predeterminada.

  • Con LLM, es más fácil que nunca escribir consultas SQL complejas.

  • Aprender SQL es un lenguaje universal y probablemente no cambiará.


 pnpm add drizzle-orm drizle-kit --filter=@repo/db


Instalar Postgres en el nivel del espacio de trabajo. Consulta la sección Problemas .

 pnma add postgres -w


Notas:

  • El indicador --filter=@repo/db le indica a pnpm que agregue el paquete al repositorio de base de datos.

17. Agregue dotenv al repositorio del espacio de trabajo

 pnpm add dotenv -w

Notas

  • El indicador -w le dice a pnpm que lo instale en el paquete package.json de la raíz

18. Agregue el Proyecto de configuración a todos los proyectos.

 pnpm add @repo/config -r --filter=!@repo/config

Notas :

  • El indicador -r le dice a pnpm que agregue el paquete a todos los repositorios.
  • El --filter=! le indica a pnpm que excluya el repositorio de configuración.
  • Tenga en cuenta el ! antes del nombre del paquete

19. (Opcional) ¿Los comandos anteriores no funcionan? Utilice .npmrc

Si pnpm extrae los paquetes del repositorio, podemos crear un archivo .npmrc en la raíz del proyecto.


.npmrc

 link-workspace-packages= true prefer-workspace-packages=true


  • Esto le indicará a pnpm que utilice primero los paquetes del espacio de trabajo.
  • Gracias a ZoWnx de Reddit, quien me ayudó a crear un archivo .nprmc

20. Configurar el tsconfig.json compartido dentro de Libs/Config

Al utilizar el poder de los espacios de trabajo de pnpm, puede crear archivos de configuración que se pueden compartir entre proyectos.


Crearemos una base tsconfig.lib.json que usaremos para nuestras bibliotecas.


Dentro de libs/config crea una instancia de tsconfig.lib.json :

 touch "libs/config/tsconfig.base.lib.json"


Luego agrega lo siguiente:

tsconfig.base.lib.json

 { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "lib": ["ES2022"], "module": "ESNext", "moduleResolution": "Bundler", "resolveJsonModule": true, "target": "ES2022", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "allowImportingTsExtensions": true, "allowJs": true, "noUncheckedIndexedAccess": true, "noEmit": true, "incremental": true, "composite": false, "declaration": true, "declarationMap": true, "inlineSources": false, "isolatedModules": true, "noUnusedLocals": false, "noUnusedParameters": false, "preserveWatchOutput": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "sourceMap": true, } } 


21. Agregue la conexión de base de datos al repositorio de base de datos.

 // libs/db/drizzle.config.ts (Yes, this one is at root of the db package, outside the src folder) // We don't want to export this file as this is ran at setup. import "dotenv/config"; // make sure to install dotenv package import { defineConfig } from "drizzle-kit"; export default defineConfig({ dialect: "postgresql", out: "./src/generated", schema: "./src/drizzle/schema.ts", dbCredentials: { url: process.env.DATABASE_URL!, }, // Print all statements verbose: true, // Always ask for confirmation strict: true, });


El archivo de esquema:

 // libs/db/src/drizzle/schema.ts export const User = pgTable("User", { userId: char("userId", { length: 26 }).primaryKey().notNull(), subId: char("subId", { length: 36 }).notNull(), // We are not making this unique to support merging accounts in later // iterations email: text("email"), loginProvider: loginProviderEnum("loginProvider").array().notNull(), createdAt: timestamp("createdAt", { precision: 3, mode: "date" }).notNull(), updatedAt: timestamp("updatedAt", { precision: 3, mode: "date" }).notNull(), });


El archivo del cliente:

 // libs/db/src/drizzle-client.ts import { drizzle, PostgresJsDatabase } from "drizzle-orm/postgres-js"; import postgres from "postgres"; import * as schema from "./schema"; export type DrizzleClient = PostgresJsDatabase<typeof schema>; let drizzleClient: DrizzleClient | undefined; type GetClientInput = { databaseUrl: string; env: string; mode?: "cloudflare" | "node"; }; declare var window: typeof globalThis; declare var self: typeof globalThis; export function getDrizzleClient(input: GetClientInput) { const { mode, env } = input; if (mode === "cloudflare") { return generateClient(input); } const globalObject = typeof globalThis !== "undefined" ? globalThis : typeof global !== "undefined" ? global : typeof window !== "undefined" ? window : self; if (env === "production") { drizzleClient = generateClient(input); } else if (globalObject) { if (!(globalObject as any).__db__) { (globalObject as any).__db__ = generateClient(input); } drizzleClient = (globalObject as any).__db__; } else { drizzleClient = generateClient(input); } return drizzleClient; } type GenerateClientInput = { databaseUrl: string; env: string; }; function generateClient(input: GenerateClientInput) { const { databaseUrl, env } = input; const isLoggingEnabled = env === "development"; // prepare: false for serverless try { const client = postgres(databaseUrl, { prepare: false }); const db = drizzle(client, { schema, logger: isLoggingEnabled }); return db; } catch (e) { console.log("ERROR", e); return undefined!; } }


El archivo de migración:

 // libs/db/src/drizzle/migrate.ts import { config } from "dotenv"; import { migrate } from "drizzle-orm/postgres-js/migrator"; import postgres from "postgres"; import { drizzle } from "drizzle-orm/postgres-js"; import "dotenv/config"; import path from "path"; config({ path: "../../../../apps/my-remix-cloudflare-app/.dev.vars" }); const ssl = process.env.ENVIRONMENT === "development" ? undefined : "require"; const databaseUrl = drizzle( postgres(`${process.env.DATABASE_URL}`, { ssl, max: 1 }) ); // Somehow the current starting path is /libs/db // Remember to have the DB running before running this script const migration = path.resolve("./src/generated"); const main = async () => { try { await migrate(databaseUrl, { migrationsFolder: migration, }); console.log("Migration complete"); } catch (error) { console.log(error); } process.exit(0); }; main();

Esto debe ejecutarse después de las migraciones.


Y exporta el cliente y el esquema en el archivo src/index.ts . Otros se ejecutan en momentos específicos.

 // libs/db/src/index.ts export * from "./drizzle/drizzle-client"; export * from "./drizzle/schema "


En su package.json , agregue drizzle-kit generate y el código para ejecutar el comando de migración:

 { "name": "@repo/db", "version": "1.0.0", "main": "./src/index.ts", "module": "./src/index.ts", "types": "./src/index.ts", "scripts": { "db:generate": "drizzle-kit generate", "db:migrate": "dotenv tsx ./drizzle/migrate", }, "exports": { ".": { "import": "./src/index.ts", "default": "./src/index.ts" } }, "dependencies": { "@repo/configs": "workspace:^", "drizzle-kit": "^0.24.1", "drizzle-orm": "^0.33.0", }, "devDependencies": { "@types/node": "^22.5.0" } }

22. Utilice el tsconfig.json compartido para libs/db y libs/utils

Cree un tsconfig.json para libs/db y libs/utils

 touch "libs/db/tsconfig.json" "libs/utils/tsconfig.json"


Luego añade a cada uno:

 { "extends": "@repo/configs/tsconfig.base.lib.json", "include": ["./src"], }
  • Vea que @repo/configs se utiliza como ruta para hacer referencia a nuestro tsconfig.base.lib.json.
  • Limpia nuestro camino.

23. Instalar TSX

TypeScript Execute (TSX) es una biblioteca alternativa a ts-node. La utilizaremos para ejecutar las migraciones de Drizzle.

 pnpm add tsx -D --filter=@repo/db

24. Agregue un .env vacío en el directorio libs/db

 touch "libs/db/.env"


Añade el siguiente contenido:

 DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" NODE_ENV="development" MODE="node" 


Debería verse algo como esto


25. Agregue el repositorio libs/db a nuestro proyecto Remix

Desde la raíz del proyecto, ejecute:

 pnpm add @repo/db --filter=@repo/my-remix-cloudflare-app


Si esto no funciona, vaya al paquete json de apps/my-remix-cloudflare-app y agregue la dependencia manualmente.

 { "name": "@repo/my-remix-cloudflare-app", "version": "1.0.0", "dependencies": { "@repo/db": "workspace:*" } }

Tenga en cuenta el workspace:* en el campo de versión. Esto le indica a pnpm que use cualquier versión del paquete en el espacio de trabajo.


Si lo instalaste a través de la CLI usando pnpm add, probablemente verás algo como workspace:^ . No debería importar siempre y cuando no aumentes las versiones de los paquetes locales.


Si lo agregó manualmente, ejecute pnpm install desde la raíz del proyecto.


Deberíamos poder consumir @repo/db en nuestro proyecto.

26. Agregue algún código compartido a nuestras utilidades:

Agregue este código al archivo libs/utils/src/index.ts :

 // libs/utils/src/index.ts export function hellowWorld() { return "Hello World!"; }


27. Instale las bibliotecas y utilidades en nuestra aplicación Remix:

 pnpm add @repo/db --filter=@repo/my-remix-cloudflare-app

28. (Opcional) Iniciar un Postgres desde un contenedor Docker

Si no tienes una instancia de Postgres en ejecución, podemos iniciar una usando docker-compose. Ten en cuenta que supongo que conoces Docker.


Cree un archivo docker-compose.yml en la raíz del proyecto.

 # Auto-generated docker-compose.yml file. version: '3.8' # Define services. services: postgres: image: postgres:latest restart: always environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres - POSTGRES_DB=postgres ports: - "5432:5432" volumes: - ./postgres-data:/var/lib/postgresql/data pgadmin: # To connect PG Admin, navigate to http://localhost:8500 use: # host.docker.internal # postgres # (username) postgres # (password) postgres image: dpage/pgadmin4 ports: - "8500:80" environment: PGADMIN_DEFAULT_EMAIL: [email protected] PGADMIN_DEFAULT_PASSWORD: admin


Luego puedes ejecutar:

 docker-compose up -d

El indicador -d le indica a docker-compose que se ejecute de forma separada para que pueda tener acceso a su terminal nuevamente.

29. Generar el esquema de la base de datos

Ahora, navegue al repositorio libs/db y ejecute db:generate .

 cd `./libs/db` && pnpm db:generate
  • Tenga en cuenta que db:generate es un alias para: drizzle-kit generate
  • Verifique que tenga el .env adecuado.
  • Además, esto supone que tienes una instancia de Postgres en ejecución.

30. Ejecutar las migraciones.

Necesitamos ejecutar las migraciones para estructurar todas las tablas dentro de nuestra base de datos.


Navega al repositorio libs/db (si no estás allí) y ejecuta db:generate .

 cd `./libs/db` && pnpm db:migrate
  • Tenga en cuenta que db:migrate es un alias para: dotenv tsx ./drizzle/migrate
  • Verifique que tenga el .env adecuado.
  • Además, esto supone que tienes una instancia de Postgres en ejecución.

31. Inserte una llamada DB dentro de su aplicación Remix.

 // apps/my-remix-cloudflare-app/app/routes/_index.tsx import type { LoaderFunctionArgs } from '@remix-run/cloudflare'; import { json, useLoaderData } from '@remix-run/react'; import { getDrizzleClient } from '@repo/db'; import { Markdown } from '~/components'; import { getFileContentWithCache } from '~/services/github.server'; import { parse } from '~/services/markdoc.server'; export async function loader({ context }: LoaderFunctionArgs) { const client = await getDrizzleClient({ databaseUrl: context.env.DATABASE_URL, env: 'development', mode: 'cloudflare', }); if (client) { const res = await client.query.User.findFirst(); console.log('res', res); } const content = await getFileContentWithCache(context, 'README.md'); return json( { content: parse(content), // user: firstUser, }, { headers: { 'Cache-Control': 'public, max-age=3600', }, }, ); } export default function Index() { const { content } = useLoaderData<typeof loader>(); return <Markdown content={content} />; }


  • Tenga en cuenta que aquí no seguimos las mejores prácticas.
  • Te aconsejo no realizar ninguna llamada a la base de datos directamente dentro del cargador, sino crear una abstracción que las llame.
  • Cloudflare presenta desafíos a la hora de configurar las variables de entorno. Se transmiten por solicitud.

32. Agrega lo siguiente en tu .dev.vars:

aplicaciones/mi-remix-cloudflare-app/.dev.vars

 DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"

33. ¡Ejecuta el Proyecto Remix!

Iniciar la instancia de postgres (si no está lista)

 docker-compose up -d


Lanzar el proyecto

 pnpm turbo dev 


Caso de uso avanzado: CQRS en GetLoadContext en Cloudflare Workers.

En mis proyectos, suelo implementar un patrón CQRS , 2. Esto queda fuera del alcance de este tutorial.


Sin embargo, dentro del contexto de carga, tiendo a inyectar un mediador (y un mensaje flash de cookie) que desacoplará toda mi aplicación Remix de mi lógica comercial.


Esto se parece a esto:

 export const getLoadContext: GetLoadContext = async ({ context, request }) => { const isEnvEmpty = Object.keys(context.cloudflare.env).length === 0; const env = isEnvEmpty ? process.env : context.cloudflare.env; const sessionFlashSecret = env.SESSION_FLASH_SECRET; const flashStorage = createCookieSessionStorage({ cookie: { name: "__flash", httpOnly: true, maxAge: 60, path: "/", sameSite: "lax", secrets: [sessionFlashSecret], secure: true, }, }); return { ...context, cloudflare: { ...context.cloudflare, env, }, dispatch: (await dispatchWithContext({ env: env as unknown as Record<string, string>, request, })) as Dispatch, flashStorage, }; };

Tenga en cuenta que se omite el código de envío. Puede encontrar más información al respecto en mi artículo sobre cómo multiplicar por diez su experiencia de desarrollo con TypeScript aquí .


Puedo eliminar Remix o utilizar otro consumidor sin alterar mi código.


Pero….


Existe un desafío adicional cuando se trabaja en una estructura monorepositorio utilizando turborepo.


Si importa un archivo TypeScript desde un paquete dentro del contexto de carga, digamos @repo/db Vite devolverá un error indicando que el archivo con extensión .ts es desconocido y no sabrá cómo procesarlo.


Esto sucede porque load-context + workspaces están fuera del gráfico de importación principal del sitio, lo que deja los archivos TypeScript fuera de juego.


El truco es usar tsx y cargarlo antes de llamar a Vite, lo que funcionará. Esto es importante porque supera las siguientes limitaciones:


Dependencias del paquete Cloudflare.


Dependencias de paquetes de Cloudflare y compilación previa

En primer lugar, ese era el paso que estaba tratando de evitar, ya que significaba que tenía que introducir un paso de compilación para cada uno de los paquetes, lo que implicaba más configuración.


Afortunadamente, esto no funcionó con Cloudflare Pages. Bibliotecas específicas, como Postgres, detectarán el entorno de ejecución y extraerán el paquete requerido.


Hay una solución alternativa: podemos usar tsx para cargar todos los archivos TypeScript y transpilarlos antes de ejecutarlos.


Se podría argumentar que este es un paso previo a la compilación, pero como todavía está en el nivel del repositorio del remix, no veo problemas significativos con este enfoque.


Para solucionar esto, agregamos tsx como dependencia:

 pnpm add tsx -D --filter=@repo/my-remix-cloudflare-app


Y luego, necesitamos modificar nuestro package.json y agregar el proceso tsx a cada uno de nuestros scripts de remix:

 { "name": "@repo/my-remix-cloudflare-app", "version": "1.0.0", "scripts": { // Other scripts omitted "build": "NODE_OPTIONS=\"--import tsx/esm\" remix vite:build", "dev": "NODE_OPTIONS=\"--import tsx/esm\" remix vite:dev", "start": "NODE_OPTIONS=\"--import tsx/esm\" wrangler pages dev ./build/client" } }

Extras

Creando un archivo .npmrc

En caso de que tenga problemas al agregar sus paquetes locales con la línea de comando, puede crear un archivo .npmrc en la raíz del proyecto.

.npmrc

 link-workspace-packages= true prefer-workspace-packages=true

Esto le indicará a pnpm que utilice primero los paquetes del espacio de trabajo.


Gracias a ZoWnx de Reddit que me ayudó a crear un archivo .nprmc

Trampas -

  1. Tenga cuidado al nombrar .client y .server en sus archivos, incluso si están en una biblioteca separada. Remix los usa para determinar si es un archivo de cliente o de servidor. El proyecto no se compila por repositorio, por lo que generará un error de importación.


  2. Si tienes problemas con paquetes multiplataforma como Postgres, es mejor instalarlo en el nivel del espacio de trabajo. Detectará la importación adecuada. Instalarlo directamente en el repositorio @repo/db dejará de funcionar al importarlo a Remix.


¡¡¡Eso es todo, amigos!!!

Repositorio de GitHub

Puedes acceder a la implementación completa aquí .

¡Sígueme en las redes sociales!

Estoy construyendo un ingeniero de pruebas automatizado en público para detectar esos errores del 1% en producción.


Comparto mis avances en:


X/Twitter @javiasilis

LinkedIn @javiasilis