paint-brush
Como criar um Monorepo com Vite, Cloudflare, Remix, PNPM e Turborepo (sem etapa de construção) por@josejaviasilis
791 leituras
791 leituras

Como criar um Monorepo com Vite, Cloudflare, Remix, PNPM e Turborepo (sem etapa de construção)

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

Muito longo; Para ler

Isso cria um monorepo para implantações Vite Cloduflare do Remix sem uma etapa de construção. Isso permite que você o estenda para vários alvos
featured image - Como criar um Monorepo com Vite, Cloudflare, Remix, PNPM e Turborepo (sem etapa de construção)
Jose Javi Asilis HackerNoon profile picture
0-item




Introdução

Eu precisava de uma maneira de usar o Remix com o Vite e o Cloudflare Workers-Pages com configuração mínima.


Vi outros repositórios, como:
  • A ,
  • ,
  • .


Mas eles tinham algumas limitações:
  1. Eu não queria pré-construí-lo, pois não queria envenenar os repositórios com mais arquivos de configuração.


  2. Cloudflare Workers/Pages tem um alvo diferente. Tornou-se complicado direcioná-lo com tsup, pois pacotes como Postgres puxariam as dependências do nó quebrando quando importados para o Remix.


  3. Eu também precisava de uma maneira de consumir diferentes alvos (Remix-Cloudflare, Node/Bun)


Mesmo assim, agradeço a eles por terem aberto o caminho para tornar isso possível!


Não deixe de ler a seção sobre armadilhas no final!

Siga-me nas redes sociais!

Estou criando uma plataforma de testes automatizados em público para detectar esses erros de 1% na produção.


Compartilho meu progresso em:


Repositório GitHub

Você pode acessar a .

Passo a passo

Requisitos

  1. NodeJS
  2. PNPM
  3. Docker (Opcional - Para exemplo de banco de dados local)


Embora isso o oriente por um novo mono-repositório, é perfeitamente válido transformar um existente em um.


Isso também pressupõe que você tenha algum conhecimento sobre repositórios mono.


Observação:

  • “at root” refere-se ao caminho inicial do seu monorepositório. Para este projeto, ele estará fora dos diretórios libs e packages .

Instalar Turborepo

O Turborepo funciona em cima dos espaços de trabalho do seu gerenciador de pacotes para gerenciar os scripts e saídas do seu projeto (ele pode até mesmo armazenar em cache sua saída). Até agora, é a única ferramenta mono-repo além do Rush (que eu não testei e não gosto) que é capaz de funcionar.


O NX não tem suporte ao Vite do Remix (até o momento em que este artigo foi escrito - 28 de agosto de 2024).


 pnpm dlx create-turbo@latest

1. Configurar espaços de trabalho do PNPM

Usaremos os recursos do espaço de trabalho do PNPM para gerenciar dependências.


No seu diretório Monorepo, crie um pnpm-workspace.yaml .


Dentro dele, adicione:
 packages: - "apps/*" - "libs/*"


Isso dirá ao pnpm que todos os repositórios ficarão dentro de apps e libs . Note que usar libs ou packages (como você pode ter visto em outro lugar) não importa.

2. Crie um package.json vazio na raiz do projeto:

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


Observe o name:@repo/main Isso nos diz que esta é a entrada principal do aplicativo. Você não precisa seguir uma convenção específica ou usar o prefixo @ . As pessoas o usam para diferenciá-lo de pacotes locais/remotos ou para facilitar o agrupamento em uma organização.

3. Crie o arquivo turbo.json na raiz do projeto:

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


O arquivo turbo.json diz ao repositório turbo como interpretar nossos comandos. Tudo o que estiver dentro da chave tasks corresponderá àqueles encontrados no pacote all.json.


Note que definimos quatro comandos. Eles correspondem aos da seção script do package.json de cada repositório. No entanto, nem todos os package.json devem implementar esses comandos.


Ex.: O comando dev será acionado pelo turbo dev , e executará todos os pacotes que dev for encontrado dentro do package.json. Se você não incluí-lo no turbo, ele não será executado.

4. Crie uma pasta apps na raiz do projeto

 mkdir apps

5. Crie um aplicativo Remix na pasta apps (ou mova um existente)

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


Quando for solicitado que você Install any dependencies with npm diga não.


  • É assim que deveria ficar

6. Renomeie o name do package.json para @repo/my-remix-cloudflare-app (ou seu nome)

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

7. Copie as dependências e devDependencies de apps/<app>/package.json para o package.json do Root

Por exemplo: <root>/pacote.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. Adicione o Turbo como uma devDependency no nível raiz (isso instalará todos os pacotes do Remix).

Verifique se o turbo está dentro do devDependencies do package.json. Se não estiver listado, execute o seguinte comando:
 pnpm add turbo -D -w


O sinalizador -w informa ao pnpm para instalá-lo na raiz do espaço de trabalho.

9. Adicione as seguintes entradas ao pacote Root.json

  • Adicione o comando dev aos scripts

  • Adicione o packageManager à opção


 { "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 se tudo está funcionando executando pnpm dev

 pnpm dev

11. Crie uma pasta Libs na raiz do projeto. Adicione config, db e utils:

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

12. Adicione um src/index.ts para cada um dos pacotes.

 touch libs/config/src/index.ts libs/db/src/index.ts libs/utils/src/index.ts
  • O arquivo index.ts será usado para exportar todos os pacotes.
  • Usaremos a pasta como ponto de entrada para deixar tudo compacto.
  • Esta é uma convenção e você pode optar por não segui-la.

13. Crie um package.json vazio e adicione o seguinte ao arquivo libs/config/package.json :

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

14. Faça o mesmo para libs/db/package.json :

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

15. E libs/utils/package.json :

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


  • Especificamos o campo “exports”. Isso informa aos outros repositórios de onde importar o pacote.
  • Especificamos o campo “name”. Ele é usado para instalar o pacote e referenciá-lo a outros repositórios.

16. (DB) - Adicione Drizzle e Postgres

Notas:

  • Estou começando a desprezar ORMs. Passei mais de 10 anos aprendendo 6 diferentes, e é um conhecimento que você não pode transferir.
  • Você tem problemas quando novas tecnologias surgem. O Prisma não suporta os trabalhadores do Cloudflare de fábrica.
  • Com LLMs, é mais fácil do que nunca escrever consultas SQL complexas.
  • Aprender SQL é uma linguagem universal e provavelmente não mudará.


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


Instale o Postgres no nível do espaço de trabalho. Veja a seção Pitfall .

 pnma add postgres -w


Notas:

  • O sinalizador --filter=@repo/db informa ao pnpm para adicionar o pacote ao repositório db.

17. Adicione o dotenv ao Repositório do Espaço de Trabalho

 pnpm add dotenv -w
Notas
  • O sinalizador -w informa ao pnpm para instalá-lo no pacote root.json

18. Adicione o Projeto de Configuração a Todos os Projetos.

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

Notas :

  • O sinalizador -r informa ao pnpm para adicionar o pacote a todos os repositórios.
  • O sinalizador --filter=! informa ao pnpm para excluir o repositório de configuração.
  • Observe o ! antes do nome do pacote

19. (Opcional) Os comandos acima não funcionam? Use .npmrc

Se o pnpm estiver puxando os pacotes do repositório, podemos criar um arquivo .npmrc na raiz do projeto.


.npmrc
 link-workspace-packages= true prefer-workspace-packages=true


  • Isso dirá ao pnpm para usar os pacotes do espaço de trabalho primeiro.
  • Obrigado ao do Reddit, que me ajudou a criar um arquivo .nprmc

20. Configure o tsconfig.json compartilhado dentro de Libs/Config

Usando o poder dos espaços de trabalho do pnpm, você pode criar arquivos de configuração que podem ser compartilhados entre projetos.


Criaremos um tsconfig.lib.json base que usaremos para nossas bibliotecas.


Dentro de libs/config instancie um tsconfig.lib.json :

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


Em seguida, adicione o seguinte: tsconfig.base.lib.json
 { "$schema": "//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. Adicione a conexão db ao repositório db.

 // 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, });


O arquivo 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(), });


O arquivo do 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!; } }


O arquivo de migração:
 // 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();
Isso deve ser executado após as migrações


E exporte o cliente e o esquema no arquivo src/index.ts . Outros são executados em momentos específicos.

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


No seu package.json , adicione o drizzle-kit generate e o código para executar o comando de migração:

 { "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. Use o tsconfig.json compartilhado para libs/db e libs/utils

Crie um tsconfig.json para libs/db e libs/utils

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


Em seguida, adicione a cada um:
 { "extends": "@repo/configs/tsconfig.base.lib.json", "include": ["./src"], }
  • Veja que @repo/configs é usado como o caminho para fazer referência ao nosso tsconfig.base.lib.json.
  • Ela torna nosso caminho limpo.

23. Instale o TSX

TypeScript Execute (TSX) é uma alternativa de biblioteca para ts-node. Usaremos isso para executar as migrações do drizzle.
 pnpm add tsx -D --filter=@repo/db

24. Adicione um .env vazio no diretório libs/db

 touch "libs/db/.env"


Adicione o seguinte conteúdo:
 DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" NODE_ENV="development" MODE="node" 


Deveria ser algo parecido com isto


25. Adicione o repositório libs/db ao nosso projeto Remix

Na raiz do projeto, execute:
 pnpm add @repo/db --filter=@repo/my-remix-cloudflare-app


Se isso não funcionar, vá para o pacote apps/my-remix-cloudflare-app e adicione a dependência manualmente.

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

Observe o workspace:* no campo version. Isso diz ao pnpm para usar qualquer versão do pacote no workspace.


Se você instalou via CLI usando pnpm add, provavelmente verá algo como workspace:^ . Não deve importar, desde que você não aumente as versões locais do pacote.


Se você adicionou isso manualmente, execute pnpm install a partir da raiz do projeto.


Deveríamos conseguir consumir o @repo/db em nosso projeto.

26. Adicione algum código compartilhado aos nossos utilitários:

Adicione este código ao arquivo libs/utils/src/index.ts :

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


27. Instale os Libs/Utils em nosso aplicativo Remix:

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

28. (Opcional) Inicie um Postgres a partir de um contêiner Docker

Se você não tiver uma instância do Postgres em execução, podemos iniciar uma usando docker-compose. Observe que estou assumindo que você conhece o Docker.


Crie um arquivo docker-compose.yml na raiz do projeto.

 # 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 //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


Então você pode executar:
 docker-compose up -d

O sinalizador -d informa ao docker-compose para ser executado desanexado para que você possa ter acesso ao seu terminal novamente.

29. Gerar o esquema do BD

Agora, navegue até o repositório libs/db e execute db:generate .

 cd `./libs/db` && pnpm db:generate
  • Observe que db:generate é um alias para: drizzle-kit generate
  • Verifique se você tem o .env apropriado.
  • Além disso, isso pressupõe que você tenha uma instância do Postgres em execução.

30. Execute as migrações.

Precisamos executar as migrações para estruturar todas as tabelas em nosso banco de dados.


Navegue até o repositório libs/db (se você não estiver lá) e execute db:generate .

 cd `./libs/db` && pnpm db:migrate
  • Observe que db:migrate é um alias para: dotenv tsx ./drizzle/migrate
  • Verifique se você tem o .env apropriado.
  • Além disso, isso pressupõe que você tenha uma instância do Postgres em execução.

31. Insira uma chamada DB dentro do seu aplicativo 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} />; }


  • Observe que não estamos seguindo as melhores práticas aqui.
  • Eu aconselho você a não fazer nenhuma chamada de banco de dados diretamente no seu carregador, mas criar uma abstração que as chame.
  • O Cloudflare é desafiador quando se trata de definir as variáveis de ambiente. Elas são passadas por solicitação

32. Adicione em seu .dev.vars o seguinte:

aplicativos/meu-remix-cloudflare-app/.dev.vars
 DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"

33. Execute o Projeto Remix!

Inicie a instância do postgres (se não estiver pronta)
 docker-compose up -d


Lançar o projeto
 pnpm turbo dev 


Caso de uso avançado - CQRS em GetLoadContext em Cloudflare Workers.

Em meus projetos, costumo implementar um , Isso está fora do escopo deste tutorial.


No entanto, dentro do contexto de carga, costumo injetar um mediador (e uma mensagem flash de cookie) que desacoplará todo o meu aplicativo Remix da minha lógica de negócios.


Isso se parece com algo assim:
 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, }; };

Note que o código de despacho é omitido. Você pode encontrar mais sobre isso no meu artigo sobre como 10x sua experiência de desenvolvimento TypeScript aqui .


Posso remover o Remix ou usar outro consumidor sem alterar meu código.


Mas….


Há um desafio adicional quando você trabalha em uma estrutura monorepo usando turborepo.


Se você importar um arquivo TypeScript de um pacote dentro do load-context, digamos que @repo/db Vite retornará um erro informando que o arquivo com extensão .ts é desconhecido e não saberá como processá-lo.


Isso acontece porque load-context + workspaces estão fora do gráfico de importação principal do site, deixando os arquivos TypeScript fora do jogo.


O truque é usar tsx e carregá-lo antes de chamar Vite, o que funcionará. Isso é importante porque supera as seguintes limitações:


Dependências do pacote Cloudflare.


Dependências do pacote Cloudflare e pré-construção

Primeiro, essa era a etapa que eu estava tentando evitar, pois significava que eu teria que introduzir uma etapa de compilação para cada um dos pacotes, o que significava mais configuração.


Felizmente, isso não funcionou para o Cloudflare Pages. Bibliotecas específicas, como Postgres, detectarão o tempo de execução e puxarão o pacote necessário.


Há uma solução alternativa: podemos usar o tsx para carregar todos os arquivos TypeScript e transpilá-los antes de executar.


Você pode argumentar que esta é uma etapa de pré-construção, mas como ainda está no nível do repositório do remix, não vejo problemas significativos com essa abordagem.


Para resolver isso, adicionamos tsx como uma dependência:
 pnpm add tsx -D --filter=@repo/my-remix-cloudflare-app


E então, precisamos modificar nosso package.json e adicionar o processo tsx a cada um dos nossos 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

Criando um arquivo .npmrc

Caso esteja tendo problemas ao adicionar seus pacotes locais com a linha de comando, você pode criar um arquivo .npmrc na raiz do projeto.

.npmrc
 link-workspace-packages= true prefer-workspace-packages=true
Isso dirá ao pnpm para usar os pacotes do espaço de trabalho primeiro.


Obrigado ao do Reddit que me ajudou a criar um arquivo .nprmc

Armadilhas -

  1. Cuidado ao nomear .client e .server em seus arquivos. Mesmo se estiver em uma biblioteca separada. O Remix usa esses para determinar se é um arquivo cliente ou servidor. O projeto não é compilado por repositório, então ele lançará um erro de importação!


  2. Se você estiver tendo problemas com pacotes multiplataforma como o Postgres, instalá-lo no nível do espaço de trabalho é melhor. Ele detectará a importação adequada. Instalá-lo diretamente no repositório @repo/db quebrará ao importá-lo para o Remix.


É isso aí, pessoal!!!

Repositório GitHub

Você pode acessar a .

Siga-me nas redes sociais!

Estou criando um engenheiro de testes automatizados em público para capturar esses erros de 1% na produção.


Compartilho meu progresso em:


바카라사이트 바카라사이트 온라인바카라