diff --git a/turbo/generators/__tests__/core-package-trpc.e2e.test.ts b/turbo/generators/__tests__/core-package-trpc.e2e.test.ts new file mode 100644 index 0000000..60310a8 --- /dev/null +++ b/turbo/generators/__tests__/core-package-trpc.e2e.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect } from "vitest"; +import { mkdtempSync, cpSync, readFileSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { execSync } from "node:child_process"; +import { join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { computeSnapshot } from "../lib/snapshot"; +import expectedSnapshot from "../__snapshots__/core-package/trpc.snapshot.json"; + +// Repo root is 2 levels up from turbo/generators/__tests__ +const REPO_ROOT = resolve(fileURLToPath(import.meta.url), "..", "..", "..", ".."); + +/** + * Strip "@repo/core-trpc" from a package.json file in the tmp tree. + * Required when simulating a fresh scaffold: core-trpc does not exist yet + * but the snapshot was captured before removal. Apps in the current tree + * still list it as a dependency. + */ +function stripCoreTrpcDep(pkgJsonPath: string): void { + const raw = readFileSync(pkgJsonPath, "utf8"); + const parsed = JSON.parse(raw) as Record>; + for (const section of ["dependencies", "devDependencies", "peerDependencies"] as const) { + if (parsed[section]?.["@repo/core-trpc"]) { + delete parsed[section]["@repo/core-trpc"]; + } + } + writeFileSync(pkgJsonPath, JSON.stringify(parsed, null, 2) + "\n"); +} + +describe("e2e: core-package trpc", () => { + it("byte-identical reconstruction matches snapshot", { timeout: 120_000 }, () => { + const tmp = mkdtempSync(join(tmpdir(), "e2e-trpc-")); + cpSync(REPO_ROOT, tmp, { + recursive: true, + filter: (src) => + !src.includes("node_modules") && + !src.includes(".turbo") && + !src.includes("packages/core-trpc"), + }); + + // Strip @repo/core-trpc from app package.json files so pnpm install + // succeeds without the package being present (simulating the post-removal state). + stripCoreTrpcDep(join(tmp, "apps", "web-next", "package.json")); + stripCoreTrpcDep(join(tmp, "apps", "web-tanstack", "package.json")); + + execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { stdio: "ignore" }); + execSync(`cd ${tmp} && pnpm turbo gen core-package --args trpc`, { stdio: "ignore" }); + const result = computeSnapshot(join(tmp, "packages/core-trpc")); + expect(result).toEqual(expectedSnapshot); + }); +}); diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts index 4803dbd..8f92713 100644 --- a/turbo/generators/config.ts +++ b/turbo/generators/config.ts @@ -642,6 +642,18 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js"; }, printEventsNextSteps, ], + trpc: () => [ + () => { + assertOptionalPackageNotPresent("core-trpc"); + return "Guard passed — packages/core-trpc does not exist yet."; + }, + ...emitTemplateTree("core-package/trpc", "packages/core-trpc"), + () => { + addToTranspilePackages("apps/web-next/next.config.mjs", "@repo/core-trpc"); + return "Added @repo/core-trpc to transpilePackages."; + }, + printTrpcNextSteps, + ], }; plop.setGenerator("core-package", { @@ -651,7 +663,7 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js"; type: "list", name: "name", message: "Which optional core package?", - choices: ["realtime", "events"], + choices: ["realtime", "events", "trpc"], }, ], actions: (answers) => { @@ -1146,6 +1158,35 @@ function printHandlerNextSteps(a: { ].join("\n"); } +function printTrpcNextSteps(): string { + return [ + "─────────────────────────────────────────────────────────────", + "@repo/core-trpc scaffolded.", + "", + "Next steps (manual wiring — not generated):", + "", + " 1. pnpm install # link the new workspace package", + "", + " 2. apps/web-next/src/app/providers.tsx:", + ' - import { NextTrpcProvider } from "@repo/core-trpc/next";', + " - wrap children: {children}", + "", + " 3. apps/web-next/src/app/api/trpc/[trpc]/route.ts:", + ' - import { fetchRequestHandler } from "@trpc/server/adapters/fetch";', + ' - import { appRouter } from "@repo/core-api";', + " - export GET and POST handlers using fetchRequestHandler", + "", + " 4. apps/web-tanstack/src/routes/__root.tsx:", + ' - import { TanstackTrpcProvider } from "@repo/core-trpc/tanstack";', + " - wrap with ", + "", + " 5. Add @repo/core-trpc to apps/web-next/package.json and apps/web-tanstack/package.json dependencies", + "", + " 6. pnpm typecheck && pnpm lint && pnpm test", + "─────────────────────────────────────────────────────────────", + ].join("\n"); +} + function printEventsNextSteps(): string { return [ "─────────────────────────────────────────────────────────────",