paint-brush
Cách tạo Monorepo bằng Vite, Cloudflare, Remix, PNPM và Turborepo (Không có bước xây dựng)từ tác giả@josejaviasilis
Bài viết mới

Cách tạo Monorepo bằng Vite, Cloudflare, Remix, PNPM và Turborepo (Không có bước xây dựng)

từ tác giả Jose Javi Asilis19m2024/08/28
Read on Terminal Reader

dài quá đọc không nổi

Điều này tạo ra một monorepo cho Vite Cloduflare Deployments của Remix mà không cần bước xây dựng. Điều này cho phép bạn mở rộng nó cho nhiều mục tiêu
featured image - Cách tạo Monorepo bằng Vite, Cloudflare, Remix, PNPM và Turborepo (Không có bước xây dựng)
Jose Javi Asilis HackerNoon profile picture
0-item




Giới thiệu

Tôi cần tìm cách sử dụng Remix với Vite và Cloudflare Workers-Pages với cấu hình tối thiểu.


Tôi đã thấy các kho lưu trữ khác, chẳng hạn như:


Nhưng chúng có một số hạn chế:

  1. Tôi không muốn xây dựng trước vì không muốn làm hỏng kho lưu trữ bằng nhiều tệp cấu hình hơn.


  2. Cloudflare Workers/Pages có mục tiêu khác. Sẽ rất khó để nhắm mục tiêu vào tsup vì các gói như Postgres sẽ kéo các phụ thuộc của nút bị hỏng khi nhập vào Remix.


  3. Tôi cũng cần một cách để sử dụng các mục tiêu khác nhau (Remix-Cloudflare, Node/Bun)


Tuy nhiên, tôi vẫn cảm ơn họ vì họ đã mở đường để biến điều này thành hiện thực!


Hãy nhớ đọc phần lưu ý ở cuối nhé!

Theo dõi tôi trên mạng xã hội!

Tôi đang xây dựng một nền tảng thử nghiệm tự động công khai để phát hiện 1% lỗi trong quá trình sản xuất.


Tôi chia sẻ tiến trình của mình về:


X/Twitter @javiasilis

Linkedin @javiasilis

Kho lưu trữ GitHub

Bạn có thể truy cập vào bản triển khai đầy đủ tại đây .

từng bước một

Yêu cầu

  1. NodeJS
  2. PNPPM-VN
  3. Docker (Tùy chọn - Đối với ví dụ cơ sở dữ liệu cục bộ)


Mặc dù cách này hướng dẫn bạn đến một kho lưu trữ đơn mới, nhưng việc chuyển đổi kho lưu trữ đơn hiện có thành kho lưu trữ đơn là hoàn toàn hợp lệ.


Điều này cũng giả định rằng bạn có một số kiến thức về mono repo.


Ghi chú:

  • “at root” đề cập đến đường dẫn bắt đầu của monorepository của bạn. Đối với dự án này, nó sẽ nằm ngoài thư mục libspackages .

Cài đặt Turborepo

Turborepo hoạt động trên không gian làm việc của trình quản lý gói để quản lý các tập lệnh và đầu ra của dự án (nó thậm chí có thể lưu trữ đệm đầu ra của bạn). Cho đến nay, đây là công cụ mono-repo duy nhất ngoài Rush (mà tôi chưa thử và không thích) có khả năng hoạt động.


NX không hỗ trợ Vite của Remix (tính đến thời điểm viết bài này - 28 tháng 8 năm 2024).


https://turbo.build/

 pnpm dlx create-turbo@latest

1. Cấu hình không gian làm việc PNPM

Chúng tôi sẽ sử dụng khả năng không gian làm việc của PNPM để quản lý các mối phụ thuộc.


Trên thư mục Monorepo của bạn, hãy tạo pnpm-workspace.yaml .


Bên trong, thêm:

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


Điều này sẽ cho pnpm biết rằng tất cả các kho lưu trữ sẽ nằm bên trong appslibs . Lưu ý rằng việc sử dụng libs hoặc packages (như bạn có thể đã thấy ở nơi khác) không quan trọng.

2. Tạo một package.json trống tại Root của Project:

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


Lưu ý name:@repo/main Điều này cho chúng ta biết rằng đây là mục nhập chính của ứng dụng. Bạn không cần phải tuân theo một quy ước cụ thể hoặc sử dụng tiền tố @ . Mọi người sử dụng nó để phân biệt với các gói cục bộ/từ xa hoặc để dễ dàng nhóm nó vào một tổ chức.

3. Tạo File turbo.json trong Thư mục gốc của Dự án:

 { "$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": {} } }


Tệp turbo.json cho biết kho turbo cách diễn giải các lệnh của chúng ta. Mọi thứ nằm trong khóa tasks sẽ khớp với những thứ được tìm thấy trong all package.json.


Lưu ý rằng chúng tôi định nghĩa bốn lệnh. Chúng khớp với các lệnh trong phần tập lệnh của package.json của mỗi kho lưu trữ. Tuy nhiên, không phải tất cả package.json đều phải triển khai các lệnh này.


Ví dụ: Lệnh dev sẽ được kích hoạt bởi turbo dev và nó sẽ thực thi tất cả các gói mà dev được tìm thấy trong package.json. Nếu bạn không đưa nó vào turbo, nó sẽ không thực thi.

4. Tạo một thư mục apps trong thư mục gốc của dự án

 mkdir apps

5. Tạo ứng dụng Remix trong thư mục apps (hoặc di chuyển ứng dụng hiện có)

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


Khi được yêu cầu Install any dependencies with npm hãy trả lời không.


  • Đây là cách nó nên trông như thế nào

6. Đổi name package.json thành @repo/my-remix-cloudflare-app (Hoặc Tên của bạn)

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

7. Sao chép Dependencies và devDependencies từ apps/<app>/package.json đến package.json của Root

Ví dụ:

<gốc>/gói.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. Thêm Turbo làm devDependency ở cấp độ gốc (Điều này sẽ cài đặt tất cả các gói của Remix).

Xác minh rằng turbo nằm trong devDependencies của package.json. Nếu không được liệt kê, hãy thực hiện lệnh sau:

 pnpm add turbo -D -w


Cờ -w yêu cầu pnpm cài đặt nó tại gốc không gian làm việc.

9. Thêm các mục sau vào Root package.json

  • Thêm lệnh dev vào scripts

  • Thêm packageManager vào tùy chọ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. Kiểm tra mọi thứ đang hoạt động bằng cách chạy pnpm dev

 pnpm dev

11. Tạo một thư mục Libs tại gốc của dự án. Thêm config, db và utils:

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

12. Thêm src/index.ts cho mỗi gói.

 touch libs/config/src/index.ts libs/db/src/index.ts libs/utils/src/index.ts
  • Tệp index.ts sẽ được sử dụng để xuất tất cả các gói.
  • Chúng ta sẽ sử dụng thư mục làm điểm vào để làm cho mọi thứ gọn gàng hơn.
  • Đây là một quy ước và bạn có thể chọn không tuân theo.

13. Tạo một package.json trống và thêm nội dung sau vào tệp libs/config/package.json :

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

14. Thực hiện tương tự cho libs/db/package.json :

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

15. Và libs/utils/package.json :

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


  • Chúng tôi chỉ định trường “exports”. Trường này cho các kho lưu trữ khác biết nơi nhập gói từ đâu.
  • Chúng tôi chỉ định trường “name”. Trường này được sử dụng để cài đặt gói và tham chiếu đến các kho lưu trữ khác.

16. (DB) - Thêm Drizzle và Postgres

Ghi chú:

  • Tôi bắt đầu ghét ORM. Tôi đã dành hơn 10 năm để học 6 loại ORM khác nhau và đó là kiến thức mà bạn không thể chuyển giao.

  • Bạn gặp vấn đề khi công nghệ mới ra đời. Prisma không hỗ trợ Cloudflare worker ngay từ đầu.

  • Với LLM, việc viết các truy vấn SQL phức tạp trở nên dễ dàng hơn bao giờ hết.

  • Học SQL là ngôn ngữ phổ biến và có khả năng sẽ không thay đổi.


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


Cài đặt Postgres ở cấp độ không gian làm việc. Xem phần Pitfall .

 pnma add postgres -w


Ghi chú:

  • Cờ --filter=@repo/db yêu cầu pnpm thêm gói vào kho lưu trữ db.

17. Thêm dotenv vào Kho lưu trữ không gian làm việc

 pnpm add dotenv -w

Ghi chú

  • Cờ -w yêu cầu pnpm cài đặt nó tại package.json của root

18. Thêm Dự án Cấu hình vào Tất cả các Dự án.

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

Ghi chú :

  • Cờ -r yêu cầu pnpm thêm gói vào tất cả các kho lưu trữ.
  • Cờ --filter=! yêu cầu pnpm loại trừ kho lưu trữ cấu hình.
  • Lưu ý dấu ! trước tên gói

19. (Tùy chọn) Các lệnh trên không hoạt động? Sử dụng .npmrc

Nếu pnpm đang kéo các gói từ kho lưu trữ, chúng ta có thể tạo tệp .npmrc ở thư mục gốc của dự án.


.npmrc

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


  • Điều này sẽ yêu cầu pnpm sử dụng các gói không gian làm việc trước.
  • Cảm ơn ZoWnx từ Reddit đã giúp tôi tạo tệp .nprmc

20. Cấu hình tsconfig.json được chia sẻ bên trong Libs/Config

Sử dụng sức mạnh của không gian làm việc pnpm, bạn có thể tạo các tệp cấu hình có thể chia sẻ giữa các dự án.


Chúng ta sẽ tạo một file tsconfig.lib.json cơ sở để sử dụng cho các thư viện của mình.


Bên trong libs/config khởi tạo tsconfig.lib.json :

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


Sau đó, thêm nội dung sau:

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. Thêm kết nối db vào kho lưu trữ 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, });


Tệp lược đồ:

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


Tệp khách hàng:

 // 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!; } }


Tệp di chuyể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();

Điều này nên được thực hiện sau khi di chuyển


Và xuất client và schema trong file src/index.ts . Những cái khác được chạy vào những thời điểm cụ thể.

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


Trong package.json của bạn, hãy thêm drizzle-kit generate và mã để chạy lệnh di chuyể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. Sử dụng tsconfig.json dùng chung cho libs/dblibs/utils

Tạo tsconfig.json cho libs/dblibs/utils

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


Sau đó thêm vào mỗi mục:

 { "extends": "@repo/configs/tsconfig.base.lib.json", "include": ["./src"], }
  • Xem @repo/configs được sử dụng làm đường dẫn để tham chiếu đến tsconfig.base.lib.json của chúng tôi.
  • Nó làm cho con đường của chúng ta sạch sẽ.

23. Cài đặt TSX

TypeScript Execute (TSX) là một thư viện thay thế cho ts-node. Chúng ta sẽ sử dụng nó để thực hiện di chuyển drizzle.

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

24. Thêm một .env trống vào thư mục libs/db

 touch "libs/db/.env"


Thêm các nội dung sau:

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


Nó sẽ trông giống như thế này


25. Thêm kho lưu trữ libs/db vào dự án Remix của chúng tôi

Từ gốc của dự án, hãy chạy:

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


Nếu cách này không hiệu quả, hãy vào package.json của apps/my-remix-cloudflare-app và thêm phần phụ thuộc theo cách thủ công.

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

Lưu ý workspace:* trong trường phiên bản. Điều này cho pnpm biết sử dụng bất kỳ phiên bản nào của gói trong không gian làm việc.


Nếu bạn cài đặt nó thông qua CLI bằng cách sử dụng pnpm add, bạn có thể sẽ thấy một cái gì đó giống như workspace:^ . Điều đó không quan trọng miễn là bạn không tăng phiên bản gói cục bộ.


Nếu bạn đã thêm thủ công, hãy chạy pnpm install từ thư mục gốc của dự án.


Chúng ta có thể sử dụng @repo/db trong dự án của mình.

26. Thêm một số mã được chia sẻ vào tiện ích của chúng tôi:

Thêm mã này vào tệp libs/utils/src/index.ts :

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


27. Cài đặt Libs/Utils vào ứng dụng Remix của chúng tôi:

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

28. (Tùy chọn) Khởi chạy Postgres từ Docker Container

Nếu bạn không có phiên bản Postgres đang chạy, chúng ta có thể khởi chạy một phiên bản bằng docker-compose. Lưu ý, tôi cho rằng bạn biết Docker.


Tạo tệp docker-compose.yml ở thư mục gốc của dự án.

 # 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


Sau đó bạn có thể chạy:

 docker-compose up -d

Cờ -d yêu cầu docker-compose chạy riêng biệt để bạn có thể truy cập lại vào thiết bị đầu cuối của mình.

29. Tạo lược đồ DB

Bây giờ, hãy điều hướng đến kho lưu trữ libs/db và chạy db:generate .

 cd `./libs/db` && pnpm db:generate
  • Lưu ý rằng db:generate là một bí danh cho: drizzle-kit generate
  • Xác minh rằng bạn có .env phù hợp.
  • Ngoài ra, điều này giả định rằng bạn có một phiên bản Postgres đang chạy.

30. Chạy di chuyển.

Chúng ta cần chạy di chuyển để tạo khung cho tất cả các bảng trong cơ sở dữ liệu của mình.


Điều hướng đến kho lưu trữ libs/db (nếu bạn không có ở đó) và chạy db:generate .

 cd `./libs/db` && pnpm db:migrate
  • Lưu ý rằng db:migrate là một bí danh cho: dotenv tsx ./drizzle/migrate
  • Xác minh rằng bạn có .env phù hợp.
  • Ngoài ra, điều này giả định rằng bạn có một phiên bản Postgres đang chạy.

31. Chèn lệnh gọi DB vào ứng dụng Remix của bạn.

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


  • Xin lưu ý rằng chúng tôi không tuân theo các biện pháp tốt nhất ở đây.
  • Tôi khuyên bạn không nên thực hiện bất kỳ lệnh gọi DB nào trực tiếp trong trình tải của mình mà hãy tạo một lớp trừu tượng để gọi chúng.
  • Cloudflare gặp khó khăn khi thiết lập các biến môi trường. Chúng được truyền theo yêu cầu

32. Thêm vào .dev.vars của bạn những thông tin sau:

ứng dụng/my-remix-cloudflare-app/.dev.vars

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

33. Thực hiện Dự án Phối lại!

Khởi chạy phiên bản postgres (nếu chưa sẵn sàng)

 docker-compose up -d


Khởi động dự án

 pnpm turbo dev 


Trường hợp sử dụng nâng cao - CQRS trong GetLoadContext trong Cloudflare Workers.

Trong các dự án của mình, tôi có xu hướng triển khai mẫu CQRS , 2. Điều này nằm ngoài phạm vi của hướng dẫn này.


Tuy nhiên, trong bối cảnh tải, tôi có xu hướng chèn một trình trung gian (và một thông báo cookie flash) để tách toàn bộ Ứng dụng Remix khỏi logic kinh doanh của tôi.


Nó trông giống như thế này:

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

Lưu ý rằng mã dispatch bị bỏ qua. Bạn có thể tìm hiểu thêm về nó trong bài viết của tôi về cách tăng gấp 10 lần trải nghiệm phát triển TypeScript của bạn tại đây .


Tôi có thể loại bỏ Remix hoặc sử dụng một trình tiêu dùng khác mà không cần thay đổi mã của mình.


Nhưng….


Có một thách thức nữa khi bạn làm việc trong cấu trúc monorepo bằng cách sử dụng turborepo.


Nếu bạn nhập tệp TypeScript từ một gói trong ngữ cảnh tải, giả sử @repo/db Vite sẽ trả về lỗi cho biết tệp có phần mở rộng .ts không xác định và không biết cách xử lý tệp đó.


Điều này xảy ra vì load-context + workspaces nằm ngoài biểu đồ nhập chính của trang web, khiến các tệp TypeScript không được sử dụng.


Mẹo là sử dụng tsx và tải nó trước khi gọi Vite, điều này sẽ hiệu quả. Điều này quan trọng vì nó khắc phục được những hạn chế sau:


Các gói phụ thuộc của Cloudflare.


Các gói phụ thuộc của Cloudflare và việc xây dựng trước

Trước hết, đó là bước mà tôi muốn tránh vì nó có nghĩa là tôi phải giới thiệu một bước xây dựng cho từng gói, nghĩa là phải cấu hình nhiều hơn.


May mắn thay, điều này không hiệu quả với Cloudflare Pages. Các thư viện cụ thể, chẳng hạn như Postgres, sẽ phát hiện thời gian chạy và kéo gói cần thiết.


Có một giải pháp thay thế: Chúng ta có thể sử dụng tsx để tải tất cả các tệp TypeScript và biên dịch chúng trước khi thực thi.


Bạn có thể cho rằng đây là bước dựng trước, nhưng vì nó vẫn ở cấp độ kho lưu trữ của bản phối lại nên tôi không thấy có vấn đề gì đáng kể với cách tiếp cận này.


Để giải quyết vấn đề này, chúng ta thêm tsx làm phụ thuộc:

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


Và sau đó, chúng ta cần sửa đổi package.json và thêm quy trình tsx vào từng tập lệnh phối lại của mình:

 { "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" } }

Thêm vào

Tạo một tập tin .npmrc

Trong trường hợp bạn gặp sự cố khi thêm các gói cục bộ bằng dòng lệnh, bạn có thể tạo tệp .npmrc trong thư mục gốc của dự án.

.npmrc

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

Điều này sẽ yêu cầu pnpm sử dụng các gói không gian làm việc trước.


Cảm ơn ZoWnx từ Reddit đã giúp tôi tạo tệp .nprmc

Những cạm bẫy -

  1. Cẩn thận khi đặt tên .client.server trong các tệp của bạn. Ngay cả khi nó nằm trong một thư viện riêng. Remix sử dụng những tên này để xác định xem đó là tệp máy khách hay máy chủ. Dự án không được biên dịch theo từng kho lưu trữ nên sẽ gây ra lỗi nhập!


  2. Nếu bạn gặp sự cố với các gói đa nền tảng như Postgres, cài đặt ở cấp độ không gian làm việc sẽ tốt hơn. Nó sẽ phát hiện ra lệnh nhập thích hợp. Cài đặt trực tiếp trong kho lưu trữ @repo/db sẽ bị hỏng khi nhập vào Remix.


Vậy là xong rồi các bạn ơi!!!

Kho lưu trữ GitHub

Bạn có thể truy cập vào bản triển khai đầy đủ tại đây .

Theo dõi tôi trên mạng xã hội!

Tôi đang xây dựng một kỹ sư thử nghiệm tự động để phát hiện 1% lỗi trong quá trình sản xuất.


Tôi chia sẻ tiến trình của mình về:


X/Twitter @javiasilis

Linkedin @javiasilis