From 0a3a2c3845578dfdf77b667b897718f38dc86c28 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Sat, 9 May 2026 14:06:14 +0200 Subject: [PATCH] feat(generators): capture core-trpc as verbatim template files Co-Authored-By: Claude Sonnet 4.6 --- .../templates/core-package/trpc/AGENTS.md.hbs | 56 +++++++++++++++++++ .../core-package/trpc/eslint.config.js.hbs | 3 + .../core-package/trpc/package.json.hbs | 37 ++++++++++++ .../core-package/trpc/src/client.test.ts.hbs | 12 ++++ .../core-package/trpc/src/client.ts.hbs | 6 ++ .../core-package/trpc/src/index.ts.hbs | 3 + .../trpc/src/providers/next-provider.tsx.hbs | 32 +++++++++++ .../src/providers/tanstack-provider.tsx.hbs | 32 +++++++++++ .../core-package/trpc/src/query-client.ts.hbs | 22 ++++++++ .../core-package/trpc/tsconfig.json.hbs | 17 ++++++ .../core-package/trpc/turbo.json.hbs | 4 ++ .../core-package/trpc/vitest.config.ts.hbs | 7 +++ 12 files changed, 231 insertions(+) create mode 100644 turbo/generators/templates/core-package/trpc/AGENTS.md.hbs create mode 100644 turbo/generators/templates/core-package/trpc/eslint.config.js.hbs create mode 100644 turbo/generators/templates/core-package/trpc/package.json.hbs create mode 100644 turbo/generators/templates/core-package/trpc/src/client.test.ts.hbs create mode 100644 turbo/generators/templates/core-package/trpc/src/client.ts.hbs create mode 100644 turbo/generators/templates/core-package/trpc/src/index.ts.hbs create mode 100644 turbo/generators/templates/core-package/trpc/src/providers/next-provider.tsx.hbs create mode 100644 turbo/generators/templates/core-package/trpc/src/providers/tanstack-provider.tsx.hbs create mode 100644 turbo/generators/templates/core-package/trpc/src/query-client.ts.hbs create mode 100644 turbo/generators/templates/core-package/trpc/tsconfig.json.hbs create mode 100644 turbo/generators/templates/core-package/trpc/turbo.json.hbs create mode 100644 turbo/generators/templates/core-package/trpc/vitest.config.ts.hbs diff --git a/turbo/generators/templates/core-package/trpc/AGENTS.md.hbs b/turbo/generators/templates/core-package/trpc/AGENTS.md.hbs new file mode 100644 index 0000000..0ae812d --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/AGENTS.md.hbs @@ -0,0 +1,56 @@ +# AGENTS.md — core-trpc + +**Tag:** core-composition + +Frontend tRPC platform providing the client and framework-specific providers (Next.js, TanStack). Bridges typed backend (`@repo/core-api`) to frontend applications. Tagged `core-composition` (not `core`) because it transitively reaches features through `core-api`'s `AppRouter` type. + +## Responsibilities + +- **Create tRPC React client** — `createTRPCReact()` for use in React apps +- **Query client factory** — TanStack React Query setup per app +- **Framework-specific providers** — Next.js App Router provider + TanStack Start provider +- **No business logic** — purely framework integration + +## Must NOT import + +- Any feature package (`@repo/auth`, `@repo/blog`, etc.) +- Any app package +- `@repo/core-api`, `@repo/core-cms`, `@repo/core-ui` + +## Public exports + +From `package.json`: +- `.` — client + query client factory +- `./next` — Next.js App Router provider +- `./tanstack` — TanStack Start provider + +Example usage: +```typescript +// In Next.js app +import { TrpcProvider } from "@repo/core-trpc/next"; + +// In TanStack Start app +import { TrpcProvider } from "@repo/core-trpc/tanstack"; + +// In client components +import { useTRPC } from "@repo/core-trpc"; +const trpc = useTRPC(); +``` + +## Test conventions + +- No unit tests (provider layer) +- Verify at app boot: `pnpm dev --filter @repo/web-next` or `pnpm dev --filter @repo/web-tanstack` succeeds +- Verify client type safety: `pnpm typecheck` confirms `AppRouter` is imported and typed correctly + +## Structure + +``` +src/ + client.ts # createTRPCReact() + query-client.ts # makeQueryClient() + providers/ + next-provider.tsx # 'use client' provider for Next.js + tanstack-provider.tsx # Provider for TanStack Start + index.ts # re-exports client + factories +``` diff --git a/turbo/generators/templates/core-package/trpc/eslint.config.js.hbs b/turbo/generators/templates/core-package/trpc/eslint.config.js.hbs new file mode 100644 index 0000000..7440d8f --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/eslint.config.js.hbs @@ -0,0 +1,3 @@ +import baseConfig from "@repo/core-eslint/base"; + +export default baseConfig; diff --git a/turbo/generators/templates/core-package/trpc/package.json.hbs b/turbo/generators/templates/core-package/trpc/package.json.hbs new file mode 100644 index 0000000..bc81356 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/package.json.hbs @@ -0,0 +1,37 @@ +{ + "name": "@repo/core-trpc", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./src/index.ts", + "./next": "./src/providers/next-provider.tsx", + "./tanstack": "./src/providers/tanstack-provider.tsx" + }, + "scripts": { + "build": "tsc --noEmit", + "lint": "eslint .", + "typecheck": "tsc --noEmit", + "test": "vitest run --passWithNoTests" + }, + "dependencies": { + "@repo/core-api": "workspace:*", + "@trpc/client": "^11.0.0", + "@trpc/react-query": "^11.0.0", + "@trpc/server": "^11.0.0", + "@trpc/tanstack-react-query": "^11.1.0", + "@tanstack/react-query": "^5.66.0", + "react": "^19.0.0", + "superjson": "^2.2.1" + }, + "devDependencies": { + "@repo/core-eslint": "workspace:*", + "@repo/core-testing": "workspace:*", + "@repo/core-typescript": "workspace:*", + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^16.0.0", + "@types/react": "^19.0.0", + "jsdom": "^25.0.0", + "vitest": "^3.0.0" + } +} diff --git a/turbo/generators/templates/core-package/trpc/src/client.test.ts.hbs b/turbo/generators/templates/core-package/trpc/src/client.test.ts.hbs new file mode 100644 index 0000000..e998a06 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/src/client.test.ts.hbs @@ -0,0 +1,12 @@ +import { describe, it, expect } from "vitest"; +import { useTRPC, TRPCProvider } from "./client"; + +describe("core-trpc client exports", () => { + it("exports useTRPC hook", () => { + expect(useTRPC).toBeTypeOf("function"); + }); + + it("exports TRPCProvider component", () => { + expect(TRPCProvider).toBeTypeOf("function"); + }); +}); diff --git a/turbo/generators/templates/core-package/trpc/src/client.ts.hbs b/turbo/generators/templates/core-package/trpc/src/client.ts.hbs new file mode 100644 index 0000000..722b8c1 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/src/client.ts.hbs @@ -0,0 +1,6 @@ +"use client"; + +import { createTRPCContext } from "@trpc/tanstack-react-query"; +import type { AppRouter } from "@repo/core-api"; + +export const { TRPCProvider, useTRPC } = createTRPCContext(); diff --git a/turbo/generators/templates/core-package/trpc/src/index.ts.hbs b/turbo/generators/templates/core-package/trpc/src/index.ts.hbs new file mode 100644 index 0000000..7b27b5d --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/src/index.ts.hbs @@ -0,0 +1,3 @@ +export { useTRPC, TRPCProvider } from "./client"; +export { getQueryClient } from "./query-client"; +export type { AppRouter } from "@repo/core-api"; diff --git a/turbo/generators/templates/core-package/trpc/src/providers/next-provider.tsx.hbs b/turbo/generators/templates/core-package/trpc/src/providers/next-provider.tsx.hbs new file mode 100644 index 0000000..22ae0b3 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/src/providers/next-provider.tsx.hbs @@ -0,0 +1,32 @@ +"use client"; + +import { useState } from "react"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { createTRPCClient, httpBatchLink } from "@trpc/client"; +import superjson from "superjson"; +import type { AppRouter } from "@repo/core-api"; +import { TRPCProvider } from "../client"; +import { getQueryClient } from "../query-client"; + +export function NextTrpcProvider({ + children, + trpcUrl = "/api/trpc", +}: { + children: React.ReactNode; + trpcUrl?: string; +}) { + const [queryClient] = useState(() => getQueryClient()); + const [trpcClient] = useState(() => + createTRPCClient({ + links: [httpBatchLink({ url: trpcUrl, transformer: superjson })], + }), + ); + + return ( + + + {children} + + + ); +} diff --git a/turbo/generators/templates/core-package/trpc/src/providers/tanstack-provider.tsx.hbs b/turbo/generators/templates/core-package/trpc/src/providers/tanstack-provider.tsx.hbs new file mode 100644 index 0000000..e950e87 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/src/providers/tanstack-provider.tsx.hbs @@ -0,0 +1,32 @@ +"use client"; + +import { useState } from "react"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { createTRPCClient, httpBatchLink } from "@trpc/client"; +import superjson from "superjson"; +import type { AppRouter } from "@repo/core-api"; +import { TRPCProvider } from "../client"; +import { getQueryClient } from "../query-client"; + +export function TanstackTrpcProvider({ + children, + trpcUrl, +}: { + children: React.ReactNode; + trpcUrl: string; +}) { + const [queryClient] = useState(() => getQueryClient()); + const [trpcClient] = useState(() => + createTRPCClient({ + links: [httpBatchLink({ url: trpcUrl, transformer: superjson })], + }), + ); + + return ( + + + {children} + + + ); +} diff --git a/turbo/generators/templates/core-package/trpc/src/query-client.ts.hbs b/turbo/generators/templates/core-package/trpc/src/query-client.ts.hbs new file mode 100644 index 0000000..fd496f0 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/src/query-client.ts.hbs @@ -0,0 +1,22 @@ +import { QueryClient } from "@tanstack/react-query"; + +let clientQueryClient: QueryClient | undefined; + +const defaultOptions = { + queries: { + staleTime: 30 * 1000, + refetchOnWindowFocus: false, + }, +}; + +export function getQueryClient(): QueryClient { + if (typeof window === "undefined") { + // Server: always create a new instance per request + return new QueryClient({ defaultOptions }); + } + // Browser: singleton + if (!clientQueryClient) { + clientQueryClient = new QueryClient({ defaultOptions }); + } + return clientQueryClient; +} diff --git a/turbo/generators/templates/core-package/trpc/tsconfig.json.hbs b/turbo/generators/templates/core-package/trpc/tsconfig.json.hbs new file mode 100644 index 0000000..cd8d483 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/tsconfig.json.hbs @@ -0,0 +1,17 @@ +{ + "extends": "@repo/core-typescript/base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": ".", + "lib": ["ES2022", "DOM"], + "jsx": "preserve", + "declaration": false, + "declarationMap": false, + "types": ["vitest/globals", "@testing-library/jest-dom"], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/turbo/generators/templates/core-package/trpc/turbo.json.hbs b/turbo/generators/templates/core-package/trpc/turbo.json.hbs new file mode 100644 index 0000000..abaadc9 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/turbo.json.hbs @@ -0,0 +1,4 @@ +{ + "extends": ["//"], + "tags": ["core-composition"] +} diff --git a/turbo/generators/templates/core-package/trpc/vitest.config.ts.hbs b/turbo/generators/templates/core-package/trpc/vitest.config.ts.hbs new file mode 100644 index 0000000..ccf910f --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/vitest.config.ts.hbs @@ -0,0 +1,7 @@ +import path from "node:path"; +import { mergeConfig } from "vitest/config"; +import { jsdomVitestConfig } from "@repo/core-typescript/vitest.base.jsdom"; + +export default mergeConfig(jsdomVitestConfig, { + resolve: { alias: { "@": path.resolve(__dirname, "./src") } }, +});