নীচের অংশে ক্ষতিকর অংশ পড়তে ভুলবেন না!
দ্রষ্টব্য:
libs
এবং packages
ডিরেক্টরির বাইরে থাকবে।
pnpm dlx create-turbo@latest
আপনার মনোরেপো ডিরেক্টরিতে, একটি pnpm-workspace.yaml
তৈরি করুন।
এর ভিতরে, যোগ করুন:
packages: - "apps/*" - "libs/*"
এটি পিএনপিএমকে বলবে যে সমস্ত সংগ্রহস্থলগুলি apps
এবং libs
ভিতরে থাকবে। মনে রাখবেন libs
বা packages
ব্যবহার করা (যেমন আপনি অন্য কোথাও দেখেছেন) কোন ব্যাপার না।
pnpm init
{ "name": "@repo/main", "version": "1.0.0", "scripts": {}, "keywords": [], "author": "", "license": "ISC" }
name:@repo/main
এটি আমাদের বলে যে এটি অ্যাপ্লিকেশনটির মূল এন্ট্রি। আপনাকে কোনো নির্দিষ্ট নিয়ম অনুসরণ করতে হবে না বা @
উপসর্গ ব্যবহার করতে হবে না। লোকেরা এটিকে স্থানীয়/দূরবর্তী প্যাকেজ থেকে আলাদা করতে বা এটিকে একটি সংস্থায় গোষ্ঠীভুক্ত করা সহজ করতে ব্যবহার করে।
turbo.json
ফাইল তৈরি করুন: { "$schema": "//turbo.build/schema.json", "tasks": { "build": {}, "dev": { "cache": false, "persistent": true }, "start": { "dependsOn": ["^build"], "persistent": true }, "preview": { "cache": false, "persistent": true }, "db:migrate": {} } }
turbo.json ফাইল টার্বো রেপোকে বলে কিভাবে আমাদের কমান্ড ব্যাখ্যা করতে হয়। tasks
কী-এর ভিতরে থাকা সমস্ত কিছুই all package.json-এ পাওয়া জিনিসগুলির সাথে মিলবে৷
যেমন: dev
কমান্ডটি turbo dev
দ্বারা ট্রিগার করা হবে, এবং এটি প্যাকেজ.json-এর মধ্যে dev
পাওয়া সমস্ত প্যাকেজ চালাবে। আপনি যদি এটি টার্বোতে অন্তর্ভুক্ত না করেন তবে এটি কার্যকর হবে না।
apps
ফোল্ডার তৈরি করুন mkdir apps
apps
ফোল্ডারে একটি রিমিক্স অ্যাপ তৈরি করুন (বা বিদ্যমান একটি সরান) npx create-remix --template edmundhung/remix-worker-template
যখন এটি আপনাকে Install any dependencies with npm
বলে তখন না বলুন।
name
পরিবর্তন করে @repo/my-remix-cloudflare-app
(বা আপনার নাম) { - "name": "my-remix-cloudflare-app", + "name": "@repo/my-remix-cloudflare-app", "version": "1.0.0", "keywords": [], "author": "", "license": "ISC" }
apps/<app>/package.json
থেকে Root-এর package.json
এ নির্ভরশীলতা এবং devDependencies কপি করুন { "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" } }
pnpm add turbo -D -w
-w
পতাকা পিএনপিএমকে ওয়ার্কস্পেস রুটে ইনস্টল করতে বলে।
scripts
dev
কমান্ড যোগ করুন
packageManager
বিকল্পে যোগ করুন
{ "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 } }
pnpm dev
চালানোর মাধ্যমে সবকিছু কাজ করছে তা যাচাই করুন pnpm dev
mkdir -p libs/config libs/db libs/utils
src/index.ts
যোগ করুন। touch libs/config/src/index.ts libs/db/src/index.ts libs/utils/src/index.ts
libs/config/package.json
ফাইলে নিম্নলিখিত যোগ করুন: { "name": "@repo/config", "version": "1.0.0", "type": "module", "exports": { ".": { "import": "./src/index.ts", "default": "./src/index.ts" } } }
libs/db/package.json
এর জন্য একই কাজ করুন: { "name": "@repo/db", "version": "1.0.0", "exports": { ".": { "import": "./src/index.ts", "default": "./src/index.ts" } } }
libs/utils/package.json
: { "name": "@repo/utils", "version": "1.0.0", "exports": { ".": { "import": "./src/index.ts", "default": "./src/index.ts" } } }
নোট:
pnpm add drizzle-orm drizle-kit --filter=@repo/db
কর্মক্ষেত্র স্তরে Postgres ইনস্টল করুন। পিটফল বিভাগটি দেখুন ।
pnma add postgres -w
নোট:
--filter=@repo/db
পতাকা pnpm-কে db সংগ্রহস্থলে প্যাকেজ যোগ করতে বলে। pnpm add dotenv -w
নোট-w
ফ্ল্যাগ পিএনপিএমকে রুট এর package.json এ ইনস্টল করতে বলে pnpm add @repo/config -r --filter=!@repo/config
নোট :
-r
পতাকা pnpm কে সমস্ত সংগ্রহস্থলে প্যাকেজ যোগ করতে বলে।--filter=!
ফ্ল্যাগ পিএনপিএমকে কনফিগার রিপোজিটরি বাদ দিতে বলে।!
প্যাকেজের নামের আগে যদি pnpm রিপোজিটরি থেকে প্যাকেজগুলি টেনে নেয়, আমরা প্রকল্পের মূলে একটি .npmrc
ফাইল তৈরি করতে পারি।
link-workspace-packages= true prefer-workspace-packages=true
tsconfig.json
কনফিগার করুন
libs/config
এর ভিতরে একটি tsconfig.lib.json
ইনস্ট্যান্টিয়েট করুন:
touch "libs/config/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, } }
// 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, });
// 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(), });
// 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!; } }
// 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();
মাইগ্রেশনের পরে এটি কার্যকর করা উচিত
এবং src/index.ts
ফাইলে ক্লায়েন্ট এবং স্কিমা রপ্তানি করুন। অন্যগুলো নির্দিষ্ট সময়ে চালানো হয়।
// libs/db/src/index.ts export * from "./drizzle/drizzle-client"; export * from "./drizzle/schema "
আপনার package.json
এ, drizzle-kit generate
যোগ করুন এবং মাইগ্রেশন কমান্ড চালানোর জন্য কোডটি যোগ করুন:
{ "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" } }
libs/db
এবং libs/utils
এর জন্য Shared tsconfig.json
ব্যবহার করুন libs/db
এবং libs/utils
এর জন্য একটি tsconfig.json তৈরি করুন
touch "libs/db/tsconfig.json" "libs/utils/tsconfig.json"
{ "extends": "@repo/configs/tsconfig.base.lib.json", "include": ["./src"], }
@repo/configs
আমাদের tsconfig.base.lib.json উল্লেখ করার জন্য পাথ হিসাবে ব্যবহার করা হয়েছে। pnpm add tsx -D --filter=@repo/db
libs/db
ডিরেক্টরিতে একটি খালি .env যোগ করুন touch "libs/db/.env"
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" NODE_ENV="development" MODE="node"
libs/db
সংগ্রহস্থল যোগ করুন pnpm add @repo/db --filter=@repo/my-remix-cloudflare-app
যদি এটি কাজ না করে, তাহলে apps/my-remix-cloudflare-app
এর package.json-এ যান এবং ম্যানুয়ালি নির্ভরতা যোগ করুন।
{ "name": "@repo/my-remix-cloudflare-app", "version": "1.0.0", "dependencies": { "@repo/db": "workspace:*" } }
workspace:*
সংস্করণ ক্ষেত্রে। এটি পিএনপিএমকে কর্মক্ষেত্রে প্যাকেজের যেকোনো সংস্করণ ব্যবহার করতে বলে।
আপনি যদি pnpm add,
আপনি সম্ভবত workspace:^
। যতক্ষণ না আপনি স্থানীয় প্যাকেজ সংস্করণগুলি না বাড়ান ততক্ষণ এটি কোন ব্যাপার না।
আপনি যদি ম্যানুয়ালি এটি যোগ করেন, তাহলে প্রকল্পের রুট থেকে pnpm install
চালান।
libs/utils/src/index.ts
ফাইলে এই কোড যোগ করুন:
// libs/utils/src/index.ts export function hellowWorld() { return "Hello World!"; }
pnpm add @repo/db --filter=@repo/my-remix-cloudflare-app
প্রকল্পের মূলে একটি docker-compose.yml
ফাইল তৈরি করুন।
# 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
docker-compose up -d
-d
পতাকা ডকার-কম্পোজকে বিচ্ছিন্নভাবে চালাতে বলে যাতে আপনি আবার আপনার টার্মিনালে অ্যাক্সেস পেতে পারেন।
এখন, libs/db সংগ্রহস্থলে নেভিগেট করুন এবং db:generate
চালান।
cd `./libs/db` && pnpm db:generate
db:generate
হল একটি উপনাম: drizzle-kit generate
libs/db সংগ্রহস্থলে নেভিগেট করুন (যদি আপনি সেখানে না থাকেন) এবং db:generate
চালান।
cd `./libs/db` && pnpm db:migrate
db:migrate
হল এর একটি উপনাম: dotenv tsx ./drizzle/migrate
// 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} />; }
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
docker-compose up -d
pnpm turbo dev
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, }; };
নোট করুন যে প্রেরণ কোডটি বাদ দেওয়া হয়েছে। এখানে আপনার টাইপস্ক্রিপ্ট ডেভের অভিজ্ঞতা কীভাবে 10x করা যায় সে সম্পর্কে আপনি আমার নিবন্ধে আরও জানতে পারেন।
আপনি যদি লোড-প্রসঙ্গের মধ্যে একটি প্যাকেজ থেকে একটি TypeScript ফাইল আমদানি করেন, তাহলে ধরা যাক @repo/db
Vite একটি ত্রুটি ফিরিয়ে দেবে যে এক্সটেনশন .ts
সহ ফাইলটি অজানা, এবং কীভাবে এটি প্রক্রিয়া করতে হয় তা জানবে না।
কৌশলটি হল tsx
ব্যবহার করা এবং Vite কল করার আগে এটি লোড করা, যা কাজ করবে। এটি গুরুত্বপূর্ণ কারণ এটি নিম্নলিখিত সীমাবদ্ধতাগুলি অতিক্রম করে:
ক্লাউডফ্লেয়ার প্যাকেজ নির্ভরতা এবং প্রি-বিল্ডিং
প্রথমত, এটি সেই পদক্ষেপ যা আমি এড়াতে চেষ্টা করছিলাম, কারণ এর অর্থ হল যে প্রতিটি প্যাকেজের জন্য আমাকে একটি বিল্ড ধাপ প্রবর্তন করতে হবে, যার অর্থ আরও কনফিগারেশন।
pnpm add tsx -D --filter=@repo/my-remix-cloudflare-app
এবং তারপর, আমাদেরকে আমাদের package.json
পরিবর্তন করতে হবে এবং আমাদের প্রতিটি রিমিক্স স্ক্রিপ্টে tsx প্রক্রিয়া যোগ করতে হবে:
{ "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" } }
.npmrc
ফাইল তৈরি করা হচ্ছে কমান্ড লাইনের সাথে আপনার স্থানীয় প্যাকেজ যোগ করার সময় আপনার সমস্যা হলে, আপনি প্রকল্পের রুটে একটি .npmrc
ফাইল তৈরি করতে পারেন।
link-workspace-packages= true prefer-workspace-packages=true
এটি পিএনপিএমকে প্রথমে ওয়ার্কস্পেস প্যাকেজগুলি ব্যবহার করতে বলবে।
আপনার ফাইলগুলিতে .client
এবং .server
নামকরণের সাথে সতর্ক থাকুন। এমনকি যদি এটি একটি পৃথক লাইব্রেরিতে থাকে। এটি একটি ক্লায়েন্ট বা সার্ভার ফাইল কিনা তা নির্ধারণ করতে রিমিক্স এগুলি ব্যবহার করে। প্রকল্পটি সংগ্রহস্থল প্রতি কম্পাইল করা হয় না তাই এটি একটি আমদানি ত্রুটি নিক্ষেপ করবে!
এটা, লোকেরা!!!