From 9ca2b97308c7baa396a46e895f23dbf04f8d5bc6 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 29 Oct 2025 23:20:54 +0200 Subject: [PATCH 01/20] fix(cli): turbo prune --docker misses tsconfig.base.json used by generated packages --- apps/cli/src/helpers/core/template-manager.ts | 12 ++++++++++++ apps/cli/templates/api/orpc/server/package.json.hbs | 4 +++- apps/cli/templates/api/orpc/server/tsconfig.json.hbs | 2 +- apps/cli/templates/api/trpc/server/package.json.hbs | 4 +++- apps/cli/templates/api/trpc/server/tsconfig.json.hbs | 2 +- .../auth/better-auth/server/base/package.json.hbs | 4 +++- .../auth/better-auth/server/base/tsconfig.json.hbs | 2 +- .../templates/backend/server/base/package.json.hbs | 1 + .../templates/backend/server/base/tsconfig.json.hbs | 2 +- apps/cli/templates/base/package.json.hbs | 5 ++++- apps/cli/templates/base/tsconfig.json.hbs | 2 +- apps/cli/templates/db/base/package.json.hbs | 4 +++- apps/cli/templates/db/base/tsconfig.json.hbs | 2 +- apps/cli/templates/packages/config/package.json.hbs | 6 ++++++ .../{base => packages/config}/tsconfig.base.json.hbs | 2 +- apps/cli/templates/packages/config/tsconfig.json | 7 +++++++ 16 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 apps/cli/templates/packages/config/package.json.hbs rename apps/cli/templates/{base => packages/config}/tsconfig.base.json.hbs (99%) create mode 100644 apps/cli/templates/packages/config/tsconfig.json diff --git a/apps/cli/src/helpers/core/template-manager.ts b/apps/cli/src/helpers/core/template-manager.ts index 6180bdc6..e2d9f934 100644 --- a/apps/cli/src/helpers/core/template-manager.ts +++ b/apps/cli/src/helpers/core/template-manager.ts @@ -272,6 +272,16 @@ async function setupApiPackage(projectDir: string, context: ProjectConfig) { } } +async function setupConfigPackage(projectDir: string, context: ProjectConfig) { + const configPackageDir = path.join(projectDir, "packages/config"); + await fs.ensureDir(configPackageDir); + + const configBaseDir = path.join(PKG_ROOT, "templates/packages/config"); + if (await fs.pathExists(configBaseDir)) { + await processAndCopyFiles("**/*", configBaseDir, configPackageDir, context); + } +} + async function setupDbPackage(projectDir: string, context: ProjectConfig) { if (context.database === "none" || context.orm === "none") return; @@ -342,6 +352,8 @@ export async function setupBackendFramework( projectDir: string, context: ProjectConfig, ) { + await setupConfigPackage(projectDir, context); + if (context.backend === "none") { return; } diff --git a/apps/cli/templates/api/orpc/server/package.json.hbs b/apps/cli/templates/api/orpc/server/package.json.hbs index f85df04d..d919922b 100644 --- a/apps/cli/templates/api/orpc/server/package.json.hbs +++ b/apps/cli/templates/api/orpc/server/package.json.hbs @@ -14,7 +14,9 @@ "scripts": { "build": "tsdown" }, - "devDependencies": {}, + "devDependencies": { + "@{{projectName}}/config": "workspace:*" + }, "peerDependencies": { "typescript": "^5" }, diff --git a/apps/cli/templates/api/orpc/server/tsconfig.json.hbs b/apps/cli/templates/api/orpc/server/tsconfig.json.hbs index 264793ae..28f5b5ec 100644 --- a/apps/cli/templates/api/orpc/server/tsconfig.json.hbs +++ b/apps/cli/templates/api/orpc/server/tsconfig.json.hbs @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.json", + "extends": "@{{projectName}}/config/tsconfig.base.json", "compilerOptions": { "declaration": true, "declarationMap": true, diff --git a/apps/cli/templates/api/trpc/server/package.json.hbs b/apps/cli/templates/api/trpc/server/package.json.hbs index 003865be..feaab140 100644 --- a/apps/cli/templates/api/trpc/server/package.json.hbs +++ b/apps/cli/templates/api/trpc/server/package.json.hbs @@ -14,7 +14,9 @@ "scripts": { "build": "tsdown" }, - "devDependencies": {}, + "devDependencies": { + "@{{projectName}}/config": "workspace:*" + }, "peerDependencies": { "typescript": "^5" } diff --git a/apps/cli/templates/api/trpc/server/tsconfig.json.hbs b/apps/cli/templates/api/trpc/server/tsconfig.json.hbs index 264793ae..28f5b5ec 100644 --- a/apps/cli/templates/api/trpc/server/tsconfig.json.hbs +++ b/apps/cli/templates/api/trpc/server/tsconfig.json.hbs @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.json", + "extends": "@{{projectName}}/config/tsconfig.base.json", "compilerOptions": { "declaration": true, "declarationMap": true, diff --git a/apps/cli/templates/auth/better-auth/server/base/package.json.hbs b/apps/cli/templates/auth/better-auth/server/base/package.json.hbs index f194f64e..2802b21f 100644 --- a/apps/cli/templates/auth/better-auth/server/base/package.json.hbs +++ b/apps/cli/templates/auth/better-auth/server/base/package.json.hbs @@ -14,7 +14,9 @@ "scripts": { "build": "tsdown" }, - "devDependencies": {}, + "devDependencies": { + "@{{projectName}}/config": "workspace:*" + }, "peerDependencies": { "typescript": "^5" } diff --git a/apps/cli/templates/auth/better-auth/server/base/tsconfig.json.hbs b/apps/cli/templates/auth/better-auth/server/base/tsconfig.json.hbs index 264793ae..28f5b5ec 100644 --- a/apps/cli/templates/auth/better-auth/server/base/tsconfig.json.hbs +++ b/apps/cli/templates/auth/better-auth/server/base/tsconfig.json.hbs @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.json", + "extends": "@{{projectName}}/config/tsconfig.base.json", "compilerOptions": { "declaration": true, "declarationMap": true, diff --git a/apps/cli/templates/backend/server/base/package.json.hbs b/apps/cli/templates/backend/server/base/package.json.hbs index e3957602..6ad1b821 100644 --- a/apps/cli/templates/backend/server/base/package.json.hbs +++ b/apps/cli/templates/backend/server/base/package.json.hbs @@ -14,6 +14,7 @@ ], {{/if}} "devDependencies": { + "@{{projectName}}/config": "workspace:*", "typescript": "^5.8.2" } } diff --git a/apps/cli/templates/backend/server/base/tsconfig.json.hbs b/apps/cli/templates/backend/server/base/tsconfig.json.hbs index 7a4b9daf..af4ef409 100644 --- a/apps/cli/templates/backend/server/base/tsconfig.json.hbs +++ b/apps/cli/templates/backend/server/base/tsconfig.json.hbs @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.json", + "extends": "@{{projectName}}/config/tsconfig.base.json", "compilerOptions": { "composite": true, "outDir": "dist", diff --git a/apps/cli/templates/base/package.json.hbs b/apps/cli/templates/base/package.json.hbs index 84d5c799..2a46af37 100644 --- a/apps/cli/templates/base/package.json.hbs +++ b/apps/cli/templates/base/package.json.hbs @@ -6,5 +6,8 @@ "apps/*", "packages/*" ], - "scripts": {} + "scripts": {}, + "devDependencies": { + "@{{projectName}}/config": "workspace:*" + } } diff --git a/apps/cli/templates/base/tsconfig.json.hbs b/apps/cli/templates/base/tsconfig.json.hbs index ffcbb947..8049e75c 100644 --- a/apps/cli/templates/base/tsconfig.json.hbs +++ b/apps/cli/templates/base/tsconfig.json.hbs @@ -1,3 +1,3 @@ { - "extends": "./tsconfig.base.json" + "extends": "@{{projectName}}/config/tsconfig.base.json" } diff --git a/apps/cli/templates/db/base/package.json.hbs b/apps/cli/templates/db/base/package.json.hbs index 342cae89..12f6566c 100644 --- a/apps/cli/templates/db/base/package.json.hbs +++ b/apps/cli/templates/db/base/package.json.hbs @@ -14,7 +14,9 @@ "scripts": { "build": "tsdown" }, - "devDependencies": {}, + "devDependencies": { + "@{{projectName}}/config": "workspace:*" + }, "peerDependencies": { "typescript": "^5" } diff --git a/apps/cli/templates/db/base/tsconfig.json.hbs b/apps/cli/templates/db/base/tsconfig.json.hbs index 264793ae..28f5b5ec 100644 --- a/apps/cli/templates/db/base/tsconfig.json.hbs +++ b/apps/cli/templates/db/base/tsconfig.json.hbs @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.json", + "extends": "@{{projectName}}/config/tsconfig.base.json", "compilerOptions": { "declaration": true, "declarationMap": true, diff --git a/apps/cli/templates/packages/config/package.json.hbs b/apps/cli/templates/packages/config/package.json.hbs new file mode 100644 index 00000000..9763e199 --- /dev/null +++ b/apps/cli/templates/packages/config/package.json.hbs @@ -0,0 +1,6 @@ +{ + "name": "@{{projectName}}/config", + "version": "0.0.0", + "private": true, + "description": "Shared configuration files for {{projectName}} monorepo" +} diff --git a/apps/cli/templates/base/tsconfig.base.json.hbs b/apps/cli/templates/packages/config/tsconfig.base.json.hbs similarity index 99% rename from apps/cli/templates/base/tsconfig.base.json.hbs rename to apps/cli/templates/packages/config/tsconfig.base.json.hbs index caebbeca..a1da881f 100644 --- a/apps/cli/templates/base/tsconfig.base.json.hbs +++ b/apps/cli/templates/packages/config/tsconfig.base.json.hbs @@ -30,4 +30,4 @@ "@cloudflare/workers-types"{{/if}} ] } -} \ No newline at end of file +} diff --git a/apps/cli/templates/packages/config/tsconfig.json b/apps/cli/templates/packages/config/tsconfig.json new file mode 100644 index 00000000..a7aa0efb --- /dev/null +++ b/apps/cli/templates/packages/config/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["."] +} From 33632451eb2da8709d2e3b23a618ff10899dbae0 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 6 Nov 2025 21:22:59 +0000 Subject: [PATCH 02/20] feat: tests --- apps/cli/test/config-package.test.ts | 534 +++++++++++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 apps/cli/test/config-package.test.ts diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts new file mode 100644 index 00000000..48e00140 --- /dev/null +++ b/apps/cli/test/config-package.test.ts @@ -0,0 +1,534 @@ +import { join } from "node:path"; +import { pathExists, readFile, readJSON } from "fs-extra"; +import { afterAll, describe, expect, it } from "vitest"; +import { + cleanupSmokeDirectory, + expectSuccess, + runTRPCTest, +} from "./test-utils"; + +describe("Config Package Feature", () => { + afterAll(async () => { + await cleanupSmokeDirectory(); + }); + + describe("Config Package Structure", () => { + it("should create config package at packages/config", async () => { + const result = await runTRPCTest({ + projectName: "config-pkg-structure", + backend: "hono", + runtime: "node", + database: "sqlite", + orm: "drizzle", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const configPkgPath = join(result.projectDir!, "packages/config"); + expect(await pathExists(configPkgPath)).toBe(true); + }); + + it("should NOT create tsconfig.base.json at root", async () => { + const result = await runTRPCTest({ + projectName: "no-root-tsconfig", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const rootTsConfigBase = join(result.projectDir!, "tsconfig.base.json"); + expect(await pathExists(rootTsConfigBase)).toBe(false); + }); + + it("should create tsconfig.json at root that extends config package", async () => { + const result = await runTRPCTest({ + projectName: "root-tsconfig", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const rootTsConfig = join(result.projectDir!, "tsconfig.json"); + expect(await pathExists(rootTsConfig)).toBe(true); + + const content = await readFile(rootTsConfig, "utf-8"); + expect(content).toContain("@root-tsconfig/config/tsconfig.base.json"); + }); + + it("should create package.json in config package", async () => { + const result = await runTRPCTest({ + projectName: "config-pkg-json", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const configPkgJson = join( + result.projectDir!, + "packages/config/package.json", + ); + expect(await pathExists(configPkgJson)).toBe(true); + + const pkgJson = await readJSON(configPkgJson); + expect(pkgJson.name).toBe("@config-pkg-json/config"); + expect(pkgJson.private).toBe(true); + }); + + it("should create tsconfig.base.json in config package", async () => { + const result = await runTRPCTest({ + projectName: "config-tsconfig-base", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const configTsConfigBase = join( + result.projectDir!, + "packages/config/tsconfig.base.json", + ); + expect(await pathExists(configTsConfigBase)).toBe(true); + + const content = await readJSON(configTsConfigBase); + expect(content.compilerOptions).toBeDefined(); + expect(content.compilerOptions.strict).toBe(true); + expect(content.compilerOptions.target).toBe("ESNext"); + }); + + it("should create tsconfig.json in config package", async () => { + const result = await runTRPCTest({ + projectName: "config-tsconfig", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const configTsConfig = join( + result.projectDir!, + "packages/config/tsconfig.json", + ); + expect(await pathExists(configTsConfig)).toBe(true); + }); + }); + + describe("Root Configuration", () => { + it("should include config package in root package.json devDependencies", async () => { + const result = await runTRPCTest({ + projectName: "root-config-dep", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + packageManager: "pnpm", + install: false, + }); + + expectSuccess(result); + const rootPkgJson = join(result.projectDir!, "package.json"); + const pkgJson = await readJSON(rootPkgJson); + + expect(pkgJson.devDependencies).toBeDefined(); + expect(pkgJson.devDependencies["@root-config-dep/config"]).toBe( + "workspace:*", + ); + }); + + it("should use workspace:* for pnpm package manager", async () => { + const result = await runTRPCTest({ + projectName: "pnpm-workspace", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + packageManager: "pnpm", + install: false, + }); + + expectSuccess(result); + const rootPkgJson = await readJSON( + join(result.projectDir!, "package.json"), + ); + expect(rootPkgJson.devDependencies["@pnpm-workspace/config"]).toBe( + "workspace:*", + ); + }); + }); + + describe("Workspace Package References", () => { + it("should configure db package to extend config package", async () => { + const result = await runTRPCTest({ + projectName: "db-config-ref", + backend: "hono", + runtime: "node", + database: "postgres", + orm: "drizzle", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const dbTsConfig = join(result.projectDir!, "packages/db/tsconfig.json"); + expect(await pathExists(dbTsConfig)).toBe(true); + + const content = await readFile(dbTsConfig, "utf-8"); + expect(content).toContain("@db-config-ref/config/tsconfig.base.json"); + }); + + it("should configure api package to extend config package", async () => { + const result = await runTRPCTest({ + projectName: "api-config-ref", + backend: "hono", + runtime: "node", + api: "trpc", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const apiTsConfig = join( + result.projectDir!, + "packages/api/tsconfig.json", + ); + expect(await pathExists(apiTsConfig)).toBe(true); + + const content = await readFile(apiTsConfig, "utf-8"); + expect(content).toContain("@api-config-ref/config/tsconfig.base.json"); + }); + + it("should configure server app to extend config package", async () => { + const result = await runTRPCTest({ + projectName: "server-config-ref", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const serverTsConfig = join( + result.projectDir!, + "apps/server/tsconfig.json", + ); + expect(await pathExists(serverTsConfig)).toBe(true); + + const content = await readFile(serverTsConfig, "utf-8"); + expect(content).toContain("@server-config-ref/config/tsconfig.base.json"); + }); + + it("should configure auth package to extend config package", async () => { + const result = await runTRPCTest({ + projectName: "auth-config-ref", + backend: "hono", + runtime: "node", + database: "postgres", + orm: "drizzle", + auth: "better-auth", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const authTsConfig = join( + result.projectDir!, + "packages/auth/tsconfig.json", + ); + expect(await pathExists(authTsConfig)).toBe(true); + + const content = await readFile(authTsConfig, "utf-8"); + expect(content).toContain("@auth-config-ref/config/tsconfig.base.json"); + }); + }); + + describe("Package Dependencies", () => { + it("should add config package to db package devDependencies", async () => { + const result = await runTRPCTest({ + projectName: "db-dep", + backend: "hono", + runtime: "node", + database: "sqlite", + orm: "drizzle", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const dbPkgJson = await readJSON( + join(result.projectDir!, "packages/db/package.json"), + ); + + expect(dbPkgJson.devDependencies).toBeDefined(); + expect(dbPkgJson.devDependencies["@db-dep/config"]).toBe("workspace:*"); + }); + + it("should add config package to api package devDependencies", async () => { + const result = await runTRPCTest({ + projectName: "api-dep", + backend: "hono", + runtime: "node", + api: "trpc", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const apiPkgJson = await readJSON( + join(result.projectDir!, "packages/api/package.json"), + ); + + expect(apiPkgJson.devDependencies).toBeDefined(); + expect(apiPkgJson.devDependencies["@api-dep/config"]).toBe("workspace:*"); + }); + + it("should add config package to server app devDependencies", async () => { + const result = await runTRPCTest({ + projectName: "server-dep", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const serverPkgJson = await readJSON( + join(result.projectDir!, "apps/server/package.json"), + ); + + expect(serverPkgJson.devDependencies).toBeDefined(); + expect(serverPkgJson.devDependencies["@server-dep/config"]).toBe( + "workspace:*", + ); + }); + + it("should add config package to auth package devDependencies", async () => { + const result = await runTRPCTest({ + projectName: "auth-dep", + backend: "hono", + runtime: "node", + database: "postgres", + orm: "drizzle", + auth: "better-auth", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const authPkgJson = await readJSON( + join(result.projectDir!, "packages/auth/package.json"), + ); + + expect(authPkgJson.devDependencies).toBeDefined(); + expect(authPkgJson.devDependencies["@auth-dep/config"]).toBe( + "workspace:*", + ); + }); + }); + + describe("Runtime-Specific Configuration", () => { + it("should include node types for node runtime", async () => { + const result = await runTRPCTest({ + projectName: "node-runtime", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const configTsConfigBase = await readJSON( + join(result.projectDir!, "packages/config/tsconfig.base.json"), + ); + + expect(configTsConfigBase.compilerOptions.types).toContain("node"); + }); + + it("should include bun types for bun runtime", async () => { + const result = await runTRPCTest({ + projectName: "bun-runtime", + backend: "hono", + runtime: "bun", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const configTsConfigBase = await readJSON( + join(result.projectDir!, "packages/config/tsconfig.base.json"), + ); + + expect(configTsConfigBase.compilerOptions.types).toContain("bun"); + }); + }); + + describe("Edge Cases", () => { + it("should create config package with backend: none", async () => { + const result = await runTRPCTest({ + projectName: "no-backend", + backend: "none", + runtime: "none", + database: "none", + orm: "none", + api: "none", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const configPkgPath = join(result.projectDir!, "packages/config"); + expect(await pathExists(configPkgPath)).toBe(true); + }); + + it("should create config package with self-hosted backend", async () => { + const result = await runTRPCTest({ + projectName: "self-backend", + backend: "self", + runtime: "none", + api: "trpc", + database: "sqlite", + orm: "drizzle", + frontend: ["next"], + install: false, + }); + + expectSuccess(result); + const configPkgPath = join(result.projectDir!, "packages/config"); + expect(await pathExists(configPkgPath)).toBe(true); + }); + + it("should create config package with convex backend", async () => { + const result = await runTRPCTest({ + projectName: "convex-backend", + backend: "convex", + runtime: "none", + database: "none", + orm: "none", + api: "none", + frontend: ["next"], + install: false, + }); + + expectSuccess(result); + const configPkgPath = join(result.projectDir!, "packages/config"); + expect(await pathExists(configPkgPath)).toBe(true); + }); + + it("should work with different APIs", async () => { + const result = await runTRPCTest({ + projectName: "orpc-api", + backend: "hono", + runtime: "node", + api: "orpc", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const apiTsConfig = join( + result.projectDir!, + "packages/api/tsconfig.json", + ); + const content = await readFile(apiTsConfig, "utf-8"); + expect(content).toContain("@orpc-api/config/tsconfig.base.json"); + }); + + it("should work with turborepo addon", async () => { + const result = await runTRPCTest({ + projectName: "with-turborepo", + backend: "hono", + runtime: "node", + frontend: ["tanstack-router"], + addons: ["turborepo"], + install: false, + }); + + expectSuccess(result); + const configPkgPath = join(result.projectDir!, "packages/config"); + expect(await pathExists(configPkgPath)).toBe(true); + + const turboJson = join(result.projectDir!, "turbo.json"); + expect(await pathExists(turboJson)).toBe(true); + }); + }); + + describe("Cross-Stack Compatibility", () => { + it("should work with full stack (hono + trpc + drizzle + better-auth)", async () => { + const result = await runTRPCTest({ + projectName: "full-stack", + backend: "hono", + runtime: "node", + database: "postgres", + orm: "drizzle", + api: "trpc", + auth: "better-auth", + frontend: ["tanstack-router"], + addons: ["turborepo"], + install: false, + }); + + expectSuccess(result); + + const packages = [ + "packages/config", + "packages/db", + "packages/api", + "packages/auth", + ]; + + for (const pkg of packages) { + const pkgPath = join(result.projectDir!, pkg); + expect(await pathExists(pkgPath)).toBe(true); + } + + const dbTsConfig = await readFile( + join(result.projectDir!, "packages/db/tsconfig.json"), + "utf-8", + ); + expect(dbTsConfig).toContain("@full-stack/config/tsconfig.base.json"); + }); + + it("should work with express backend", async () => { + const result = await runTRPCTest({ + projectName: "express-stack", + backend: "express", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const serverTsConfig = await readFile( + join(result.projectDir!, "apps/server/tsconfig.json"), + "utf-8", + ); + expect(serverTsConfig).toContain( + "@express-stack/config/tsconfig.base.json", + ); + }); + + it("should work with fastify backend", async () => { + const result = await runTRPCTest({ + projectName: "fastify-stack", + backend: "fastify", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }); + + expectSuccess(result); + const serverTsConfig = await readFile( + join(result.projectDir!, "apps/server/tsconfig.json"), + "utf-8", + ); + expect(serverTsConfig).toContain( + "@fastify-stack/config/tsconfig.base.json", + ); + }); + }); +}); From e14b2c60d26bc11447dca297eb80e14c9f2837e1 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 6 Nov 2025 21:29:23 +0000 Subject: [PATCH 03/20] feat: tests --- apps/cli/test/config-package.test.ts | 161 ++++++++++++++++++--------- 1 file changed, 111 insertions(+), 50 deletions(-) diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index 48e00140..92dc0e2a 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -25,7 +25,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const configPkgPath = join(result.projectDir!, "packages/config"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); }); @@ -39,7 +42,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const rootTsConfigBase = join(result.projectDir!, "tsconfig.base.json"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const rootTsConfigBase = join(projectDir, "tsconfig.base.json"); expect(await pathExists(rootTsConfigBase)).toBe(false); }); @@ -53,7 +59,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const rootTsConfig = join(result.projectDir!, "tsconfig.json"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const rootTsConfig = join(projectDir, "tsconfig.json"); expect(await pathExists(rootTsConfig)).toBe(true); const content = await readFile(rootTsConfig, "utf-8"); @@ -70,10 +79,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const configPkgJson = join( - result.projectDir!, - "packages/config/package.json", - ); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const configPkgJson = join(projectDir, "packages/config/package.json"); expect(await pathExists(configPkgJson)).toBe(true); const pkgJson = await readJSON(configPkgJson); @@ -91,8 +100,11 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const configTsConfigBase = join( - result.projectDir!, + projectDir, "packages/config/tsconfig.base.json", ); expect(await pathExists(configTsConfigBase)).toBe(true); @@ -113,10 +125,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const configTsConfig = join( - result.projectDir!, - "packages/config/tsconfig.json", - ); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const configTsConfig = join(projectDir, "packages/config/tsconfig.json"); expect(await pathExists(configTsConfig)).toBe(true); }); }); @@ -133,7 +145,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const rootPkgJson = join(result.projectDir!, "package.json"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const rootPkgJson = join(projectDir, "package.json"); const pkgJson = await readJSON(rootPkgJson); expect(pkgJson.devDependencies).toBeDefined(); @@ -153,9 +168,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const rootPkgJson = await readJSON( - join(result.projectDir!, "package.json"), - ); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const rootPkgJson = await readJSON(join(projectDir, "package.json")); expect(rootPkgJson.devDependencies["@pnpm-workspace/config"]).toBe( "workspace:*", ); @@ -175,7 +191,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const dbTsConfig = join(result.projectDir!, "packages/db/tsconfig.json"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const dbTsConfig = join(projectDir, "packages/db/tsconfig.json"); expect(await pathExists(dbTsConfig)).toBe(true); const content = await readFile(dbTsConfig, "utf-8"); @@ -193,10 +212,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const apiTsConfig = join( - result.projectDir!, - "packages/api/tsconfig.json", - ); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const apiTsConfig = join(projectDir, "packages/api/tsconfig.json"); expect(await pathExists(apiTsConfig)).toBe(true); const content = await readFile(apiTsConfig, "utf-8"); @@ -213,14 +232,16 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const serverTsConfig = join( - result.projectDir!, - "apps/server/tsconfig.json", - ); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const serverTsConfig = join(projectDir, "apps/server/tsconfig.json"); expect(await pathExists(serverTsConfig)).toBe(true); const content = await readFile(serverTsConfig, "utf-8"); - expect(content).toContain("@server-config-ref/config/tsconfig.base.json"); + expect(content).toContain( + "@server-config-ref/config/tsconfig.base.json", + ); }); it("should configure auth package to extend config package", async () => { @@ -236,10 +257,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const authTsConfig = join( - result.projectDir!, - "packages/auth/tsconfig.json", - ); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const authTsConfig = join(projectDir, "packages/auth/tsconfig.json"); expect(await pathExists(authTsConfig)).toBe(true); const content = await readFile(authTsConfig, "utf-8"); @@ -260,8 +281,11 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const dbPkgJson = await readJSON( - join(result.projectDir!, "packages/db/package.json"), + join(projectDir, "packages/db/package.json"), ); expect(dbPkgJson.devDependencies).toBeDefined(); @@ -279,12 +303,17 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const apiPkgJson = await readJSON( - join(result.projectDir!, "packages/api/package.json"), + join(projectDir, "packages/api/package.json"), ); expect(apiPkgJson.devDependencies).toBeDefined(); - expect(apiPkgJson.devDependencies["@api-dep/config"]).toBe("workspace:*"); + expect(apiPkgJson.devDependencies["@api-dep/config"]).toBe( + "workspace:*", + ); }); it("should add config package to server app devDependencies", async () => { @@ -297,8 +326,11 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const serverPkgJson = await readJSON( - join(result.projectDir!, "apps/server/package.json"), + join(projectDir, "apps/server/package.json"), ); expect(serverPkgJson.devDependencies).toBeDefined(); @@ -320,8 +352,11 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const authPkgJson = await readJSON( - join(result.projectDir!, "packages/auth/package.json"), + join(projectDir, "packages/auth/package.json"), ); expect(authPkgJson.devDependencies).toBeDefined(); @@ -342,8 +377,11 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const configTsConfigBase = await readJSON( - join(result.projectDir!, "packages/config/tsconfig.base.json"), + join(projectDir, "packages/config/tsconfig.base.json"), ); expect(configTsConfigBase.compilerOptions.types).toContain("node"); @@ -359,8 +397,11 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const configTsConfigBase = await readJSON( - join(result.projectDir!, "packages/config/tsconfig.base.json"), + join(projectDir, "packages/config/tsconfig.base.json"), ); expect(configTsConfigBase.compilerOptions.types).toContain("bun"); @@ -381,7 +422,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const configPkgPath = join(result.projectDir!, "packages/config"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); }); @@ -398,7 +442,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const configPkgPath = join(result.projectDir!, "packages/config"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); }); @@ -415,7 +462,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const configPkgPath = join(result.projectDir!, "packages/config"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); }); @@ -430,10 +480,10 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const apiTsConfig = join( - result.projectDir!, - "packages/api/tsconfig.json", - ); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const apiTsConfig = join(projectDir, "packages/api/tsconfig.json"); const content = await readFile(apiTsConfig, "utf-8"); expect(content).toContain("@orpc-api/config/tsconfig.base.json"); }); @@ -449,10 +499,13 @@ describe("Config Package Feature", () => { }); expectSuccess(result); - const configPkgPath = join(result.projectDir!, "packages/config"); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + + const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); - const turboJson = join(result.projectDir!, "turbo.json"); + const turboJson = join(projectDir, "turbo.json"); expect(await pathExists(turboJson)).toBe(true); }); }); @@ -473,6 +526,8 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; const packages = [ "packages/config", @@ -482,12 +537,12 @@ describe("Config Package Feature", () => { ]; for (const pkg of packages) { - const pkgPath = join(result.projectDir!, pkg); + const pkgPath = join(projectDir, pkg); expect(await pathExists(pkgPath)).toBe(true); } const dbTsConfig = await readFile( - join(result.projectDir!, "packages/db/tsconfig.json"), + join(projectDir, "packages/db/tsconfig.json"), "utf-8", ); expect(dbTsConfig).toContain("@full-stack/config/tsconfig.base.json"); @@ -503,8 +558,11 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const serverTsConfig = await readFile( - join(result.projectDir!, "apps/server/tsconfig.json"), + join(projectDir, "apps/server/tsconfig.json"), "utf-8", ); expect(serverTsConfig).toContain( @@ -522,8 +580,11 @@ describe("Config Package Feature", () => { }); expectSuccess(result); + expect(result.projectDir).toBeDefined(); + const projectDir = result.projectDir as string; + const serverTsConfig = await readFile( - join(result.projectDir!, "apps/server/tsconfig.json"), + join(projectDir, "apps/server/tsconfig.json"), "utf-8", ); expect(serverTsConfig).toContain( From f6d9d417dfb92afd174d3a7ec5d22a90ec8d780d Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 6 Nov 2025 21:56:12 +0000 Subject: [PATCH 04/20] feat: tests --- apps/cli/test/config-package.test.ts | 292 ++++++++++++--------------- apps/cli/test/test-utils.ts | 17 ++ 2 files changed, 151 insertions(+), 158 deletions(-) diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index 92dc0e2a..7e2e2bf5 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -3,7 +3,9 @@ import { pathExists, readFile, readJSON } from "fs-extra"; import { afterAll, describe, expect, it } from "vitest"; import { cleanupSmokeDirectory, - expectSuccess, + configPackageName, + configTsConfigReference, + expectSuccessWithProjectDir, runTRPCTest, } from "./test-utils"; @@ -24,12 +26,9 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const configPkgPath = join(projectDir, "packages/config"); - expect(await pathExists(configPkgPath)).toBe(true); + expect(await pathExists(join(projectDir, "packages/config"))).toBe(true); }); it("should NOT create tsconfig.base.json at root", async () => { @@ -41,52 +40,51 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const rootTsConfigBase = join(projectDir, "tsconfig.base.json"); - expect(await pathExists(rootTsConfigBase)).toBe(false); + expect(await pathExists(join(projectDir, "tsconfig.base.json"))).toBe( + false, + ); }); it("should create tsconfig.json at root that extends config package", async () => { + const projectName = "root-tsconfig"; const result = await runTRPCTest({ - projectName: "root-tsconfig", + projectName, backend: "hono", runtime: "node", frontend: ["tanstack-router"], install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const rootTsConfig = join(projectDir, "tsconfig.json"); - expect(await pathExists(rootTsConfig)).toBe(true); + expect(await pathExists(join(projectDir, "tsconfig.json"))).toBe(true); - const content = await readFile(rootTsConfig, "utf-8"); - expect(content).toContain("@root-tsconfig/config/tsconfig.base.json"); + const content = await readFile(join(projectDir, "tsconfig.json"), "utf-8"); + expect(content).toContain(configTsConfigReference(projectName)); }); it("should create package.json in config package", async () => { + const projectName = "config-pkg-json"; const result = await runTRPCTest({ - projectName: "config-pkg-json", + projectName, backend: "hono", runtime: "node", frontend: ["tanstack-router"], install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const configPkgJson = join(projectDir, "packages/config/package.json"); - expect(await pathExists(configPkgJson)).toBe(true); + expect( + await pathExists(join(projectDir, "packages/config/package.json")), + ).toBe(true); - const pkgJson = await readJSON(configPkgJson); - expect(pkgJson.name).toBe("@config-pkg-json/config"); + const pkgJson = await readJSON( + join(projectDir, "packages/config/package.json"), + ); + expect(pkgJson.name).toBe(configPackageName(projectName)); expect(pkgJson.private).toBe(true); }); @@ -99,17 +97,15 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const configTsConfigBase = join( - projectDir, - "packages/config/tsconfig.base.json", - ); - expect(await pathExists(configTsConfigBase)).toBe(true); + expect( + await pathExists(join(projectDir, "packages/config/tsconfig.base.json")), + ).toBe(true); - const content = await readJSON(configTsConfigBase); + const content = await readJSON( + join(projectDir, "packages/config/tsconfig.base.json"), + ); expect(content.compilerOptions).toBeDefined(); expect(content.compilerOptions.strict).toBe(true); expect(content.compilerOptions.target).toBe("ESNext"); @@ -124,19 +120,19 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const configTsConfig = join(projectDir, "packages/config/tsconfig.json"); - expect(await pathExists(configTsConfig)).toBe(true); + expect( + await pathExists(join(projectDir, "packages/config/tsconfig.json")), + ).toBe(true); }); }); describe("Root Configuration", () => { it("should include config package in root package.json devDependencies", async () => { + const projectName = "root-config-dep"; const result = await runTRPCTest({ - projectName: "root-config-dep", + projectName, backend: "hono", runtime: "node", frontend: ["tanstack-router"], @@ -144,22 +140,19 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; - - const rootPkgJson = join(projectDir, "package.json"); - const pkgJson = await readJSON(rootPkgJson); + const projectDir = expectSuccessWithProjectDir(result); + const pkgJson = await readJSON(join(projectDir, "package.json")); expect(pkgJson.devDependencies).toBeDefined(); - expect(pkgJson.devDependencies["@root-config-dep/config"]).toBe( + expect(pkgJson.devDependencies[configPackageName(projectName)]).toBe( "workspace:*", ); }); it("should use workspace:* for pnpm package manager", async () => { + const projectName = "pnpm-workspace"; const result = await runTRPCTest({ - projectName: "pnpm-workspace", + projectName, backend: "hono", runtime: "node", frontend: ["tanstack-router"], @@ -167,12 +160,10 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); + const pkgJson = await readJSON(join(projectDir, "package.json")); - const rootPkgJson = await readJSON(join(projectDir, "package.json")); - expect(rootPkgJson.devDependencies["@pnpm-workspace/config"]).toBe( + expect(pkgJson.devDependencies[configPackageName(projectName)]).toBe( "workspace:*", ); }); @@ -180,8 +171,9 @@ describe("Config Package Feature", () => { describe("Workspace Package References", () => { it("should configure db package to extend config package", async () => { + const projectName = "db-config-ref"; const result = await runTRPCTest({ - projectName: "db-config-ref", + projectName, backend: "hono", runtime: "node", database: "postgres", @@ -190,20 +182,23 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const dbTsConfig = join(projectDir, "packages/db/tsconfig.json"); - expect(await pathExists(dbTsConfig)).toBe(true); + expect( + await pathExists(join(projectDir, "packages/db/tsconfig.json")), + ).toBe(true); - const content = await readFile(dbTsConfig, "utf-8"); - expect(content).toContain("@db-config-ref/config/tsconfig.base.json"); + const content = await readFile( + join(projectDir, "packages/db/tsconfig.json"), + "utf-8", + ); + expect(content).toContain(configTsConfigReference(projectName)); }); it("should configure api package to extend config package", async () => { + const projectName = "api-config-ref"; const result = await runTRPCTest({ - projectName: "api-config-ref", + projectName, backend: "hono", runtime: "node", api: "trpc", @@ -211,42 +206,46 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const apiTsConfig = join(projectDir, "packages/api/tsconfig.json"); - expect(await pathExists(apiTsConfig)).toBe(true); + expect( + await pathExists(join(projectDir, "packages/api/tsconfig.json")), + ).toBe(true); - const content = await readFile(apiTsConfig, "utf-8"); - expect(content).toContain("@api-config-ref/config/tsconfig.base.json"); + const content = await readFile( + join(projectDir, "packages/api/tsconfig.json"), + "utf-8", + ); + expect(content).toContain(configTsConfigReference(projectName)); }); it("should configure server app to extend config package", async () => { + const projectName = "server-config-ref"; const result = await runTRPCTest({ - projectName: "server-config-ref", + projectName, backend: "hono", runtime: "node", frontend: ["tanstack-router"], install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const serverTsConfig = join(projectDir, "apps/server/tsconfig.json"); - expect(await pathExists(serverTsConfig)).toBe(true); + expect( + await pathExists(join(projectDir, "apps/server/tsconfig.json")), + ).toBe(true); - const content = await readFile(serverTsConfig, "utf-8"); - expect(content).toContain( - "@server-config-ref/config/tsconfig.base.json", + const content = await readFile( + join(projectDir, "apps/server/tsconfig.json"), + "utf-8", ); + expect(content).toContain(configTsConfigReference(projectName)); }); it("should configure auth package to extend config package", async () => { + const projectName = "auth-config-ref"; const result = await runTRPCTest({ - projectName: "auth-config-ref", + projectName, backend: "hono", runtime: "node", database: "postgres", @@ -256,22 +255,25 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const authTsConfig = join(projectDir, "packages/auth/tsconfig.json"); - expect(await pathExists(authTsConfig)).toBe(true); + expect( + await pathExists(join(projectDir, "packages/auth/tsconfig.json")), + ).toBe(true); - const content = await readFile(authTsConfig, "utf-8"); - expect(content).toContain("@auth-config-ref/config/tsconfig.base.json"); + const content = await readFile( + join(projectDir, "packages/auth/tsconfig.json"), + "utf-8", + ); + expect(content).toContain(configTsConfigReference(projectName)); }); }); describe("Package Dependencies", () => { it("should add config package to db package devDependencies", async () => { + const projectName = "db-dep"; const result = await runTRPCTest({ - projectName: "db-dep", + projectName, backend: "hono", runtime: "node", database: "sqlite", @@ -280,21 +282,21 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; - + const projectDir = expectSuccessWithProjectDir(result); const dbPkgJson = await readJSON( join(projectDir, "packages/db/package.json"), ); expect(dbPkgJson.devDependencies).toBeDefined(); - expect(dbPkgJson.devDependencies["@db-dep/config"]).toBe("workspace:*"); + expect(dbPkgJson.devDependencies[configPackageName(projectName)]).toBe( + "workspace:*", + ); }); it("should add config package to api package devDependencies", async () => { + const projectName = "api-dep"; const result = await runTRPCTest({ - projectName: "api-dep", + projectName, backend: "hono", runtime: "node", api: "trpc", @@ -302,46 +304,42 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; - + const projectDir = expectSuccessWithProjectDir(result); const apiPkgJson = await readJSON( join(projectDir, "packages/api/package.json"), ); expect(apiPkgJson.devDependencies).toBeDefined(); - expect(apiPkgJson.devDependencies["@api-dep/config"]).toBe( + expect(apiPkgJson.devDependencies[configPackageName(projectName)]).toBe( "workspace:*", ); }); it("should add config package to server app devDependencies", async () => { + const projectName = "server-dep"; const result = await runTRPCTest({ - projectName: "server-dep", + projectName, backend: "hono", runtime: "node", frontend: ["tanstack-router"], install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; - + const projectDir = expectSuccessWithProjectDir(result); const serverPkgJson = await readJSON( join(projectDir, "apps/server/package.json"), ); expect(serverPkgJson.devDependencies).toBeDefined(); - expect(serverPkgJson.devDependencies["@server-dep/config"]).toBe( + expect(serverPkgJson.devDependencies[configPackageName(projectName)]).toBe( "workspace:*", ); }); it("should add config package to auth package devDependencies", async () => { + const projectName = "auth-dep"; const result = await runTRPCTest({ - projectName: "auth-dep", + projectName, backend: "hono", runtime: "node", database: "postgres", @@ -351,16 +349,13 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; - + const projectDir = expectSuccessWithProjectDir(result); const authPkgJson = await readJSON( join(projectDir, "packages/auth/package.json"), ); expect(authPkgJson.devDependencies).toBeDefined(); - expect(authPkgJson.devDependencies["@auth-dep/config"]).toBe( + expect(authPkgJson.devDependencies[configPackageName(projectName)]).toBe( "workspace:*", ); }); @@ -376,9 +371,7 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const configTsConfigBase = await readJSON( join(projectDir, "packages/config/tsconfig.base.json"), @@ -396,9 +389,7 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const configTsConfigBase = await readJSON( join(projectDir, "packages/config/tsconfig.base.json"), @@ -421,9 +412,7 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); @@ -441,9 +430,7 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); @@ -461,17 +448,16 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); }); it("should work with different APIs", async () => { + const projectName = "orpc-api"; const result = await runTRPCTest({ - projectName: "orpc-api", + projectName, backend: "hono", runtime: "node", api: "orpc", @@ -479,13 +465,13 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); - const apiTsConfig = join(projectDir, "packages/api/tsconfig.json"); - const content = await readFile(apiTsConfig, "utf-8"); - expect(content).toContain("@orpc-api/config/tsconfig.base.json"); + const content = await readFile( + join(projectDir, "packages/api/tsconfig.json"), + "utf-8", + ); + expect(content).toContain(configTsConfigReference(projectName)); }); it("should work with turborepo addon", async () => { @@ -498,9 +484,7 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const configPkgPath = join(projectDir, "packages/config"); expect(await pathExists(configPkgPath)).toBe(true); @@ -512,8 +496,9 @@ describe("Config Package Feature", () => { describe("Cross-Stack Compatibility", () => { it("should work with full stack (hono + trpc + drizzle + better-auth)", async () => { + const projectName = "full-stack"; const result = await runTRPCTest({ - projectName: "full-stack", + projectName, backend: "hono", runtime: "node", database: "postgres", @@ -525,9 +510,7 @@ describe("Config Package Feature", () => { install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const packages = [ "packages/config", @@ -537,59 +520,52 @@ describe("Config Package Feature", () => { ]; for (const pkg of packages) { - const pkgPath = join(projectDir, pkg); - expect(await pathExists(pkgPath)).toBe(true); + expect(await pathExists(join(projectDir, pkg))).toBe(true); } const dbTsConfig = await readFile( join(projectDir, "packages/db/tsconfig.json"), "utf-8", ); - expect(dbTsConfig).toContain("@full-stack/config/tsconfig.base.json"); + expect(dbTsConfig).toContain(configTsConfigReference(projectName)); }); it("should work with express backend", async () => { + const projectName = "express-stack"; const result = await runTRPCTest({ - projectName: "express-stack", + projectName, backend: "express", runtime: "node", frontend: ["tanstack-router"], install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const serverTsConfig = await readFile( join(projectDir, "apps/server/tsconfig.json"), "utf-8", ); - expect(serverTsConfig).toContain( - "@express-stack/config/tsconfig.base.json", - ); + expect(serverTsConfig).toContain(configTsConfigReference(projectName)); }); it("should work with fastify backend", async () => { + const projectName = "fastify-stack"; const result = await runTRPCTest({ - projectName: "fastify-stack", + projectName, backend: "fastify", runtime: "node", frontend: ["tanstack-router"], install: false, }); - expectSuccess(result); - expect(result.projectDir).toBeDefined(); - const projectDir = result.projectDir as string; + const projectDir = expectSuccessWithProjectDir(result); const serverTsConfig = await readFile( join(projectDir, "apps/server/tsconfig.json"), "utf-8", ); - expect(serverTsConfig).toContain( - "@fastify-stack/config/tsconfig.base.json", - ); + expect(serverTsConfig).toContain(configTsConfigReference(projectName)); }); }); }); diff --git a/apps/cli/test/test-utils.ts b/apps/cli/test/test-utils.ts index 985987f5..053da092 100644 --- a/apps/cli/test/test-utils.ts +++ b/apps/cli/test/test-utils.ts @@ -171,6 +171,23 @@ export function expectSuccess(result: TestResult) { expect(result.result).toBeDefined(); } +export function expectSuccessWithProjectDir(result: TestResult): string { + expectSuccess(result); + expect(result.projectDir).toBeDefined(); + return result.projectDir as string; +} + +/** + * Helper functions for generating expected config package references + */ +export function configPackageName(projectName: string): string { + return `@${projectName}/config`; +} + +export function configTsConfigReference(projectName: string): string { + return `@${projectName}/config/tsconfig.base.json`; +} + export function expectError(result: TestResult, expectedMessage?: string) { expect(result.success).toBe(false); if (expectedMessage) { From 3a0b89babab22a5dff29fdd0404ebdb3d10852af Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 6 Nov 2025 22:08:05 +0000 Subject: [PATCH 05/20] feat: tests --- apps/cli/test/config-package.test.ts | 100 ++++++++++++++++++++++ apps/cli/test/test-utils.ts | 122 +++++++++++++++++++++++++++ 2 files changed, 222 insertions(+) diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index 7e2e2bf5..27ce01d5 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -7,6 +7,8 @@ import { configTsConfigReference, expectSuccessWithProjectDir, runTRPCTest, + validateConfigPackageSetup, + type TestConfig, } from "./test-utils"; describe("Config Package Feature", () => { @@ -568,4 +570,102 @@ describe("Config Package Feature", () => { expect(serverTsConfig).toContain(configTsConfigReference(projectName)); }); }); + + describe("Comprehensive Config Validation (Using Helper)", () => { + it("should validate basic stack configuration", async () => { + const projectName = "basic-stack"; + const config: TestConfig = { + projectName, + backend: "hono", + runtime: "node", + database: "sqlite", + orm: "drizzle", + api: "trpc", + frontend: ["tanstack-router"], + install: false, + }; + + const result = await runTRPCTest(config); + const projectDir = expectSuccessWithProjectDir(result); + + await validateConfigPackageSetup(projectDir, projectName, config); + }); + + it("should validate full stack with auth", async () => { + const projectName = "full-stack-helper"; + const config: TestConfig = { + projectName, + backend: "hono", + runtime: "bun", + database: "postgres", + orm: "drizzle", + api: "trpc", + auth: "better-auth", + frontend: ["tanstack-router"], + addons: ["turborepo"], + install: false, + }; + + const result = await runTRPCTest(config); + const projectDir = expectSuccessWithProjectDir(result); + + await validateConfigPackageSetup(projectDir, projectName, config); + }); + + it("should validate minimal frontend-only stack", async () => { + const projectName = "frontend-only"; + const config: TestConfig = { + projectName, + backend: "none", + runtime: "none", + database: "none", + orm: "none", + api: "none", + auth: "none", + frontend: ["tanstack-router"], + install: false, + }; + + const result = await runTRPCTest(config); + const projectDir = expectSuccessWithProjectDir(result); + + await validateConfigPackageSetup(projectDir, projectName, config); + }); + + it("should validate different backends and runtimes", async () => { + const configs: TestConfig[] = [ + { + projectName: "express-node", + backend: "express", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }, + { + projectName: "hono-bun", + backend: "hono", + runtime: "bun", + frontend: ["tanstack-router"], + install: false, + }, + { + projectName: "fastify-node", + backend: "fastify", + runtime: "node", + frontend: ["tanstack-router"], + install: false, + }, + ]; + + for (const config of configs) { + const result = await runTRPCTest(config); + const projectDir = expectSuccessWithProjectDir(result); + await validateConfigPackageSetup( + projectDir, + config.projectName as string, + config, + ); + } + }); + }); }); diff --git a/apps/cli/test/test-utils.ts b/apps/cli/test/test-utils.ts index 053da092..a10e80cb 100644 --- a/apps/cli/test/test-utils.ts +++ b/apps/cli/test/test-utils.ts @@ -188,6 +188,128 @@ export function configTsConfigReference(projectName: string): string { return `@${projectName}/config/tsconfig.base.json`; } +/** + * Comprehensive validation helper for config package setup + * Validates all expected files and references based on the CreateInput configuration + */ +export async function validateConfigPackageSetup( + projectDir: string, + projectName: string, + config: CreateInput, +): Promise { + const { pathExists, readFile, readJSON } = await import("fs-extra"); + const { join } = await import("node:path"); + const { expect } = await import("vitest"); + + // 1. Config package structure + expect(await pathExists(join(projectDir, "packages/config"))).toBe(true); + + // 2. Config package.json + const configPkgJson = await readJSON( + join(projectDir, "packages/config/package.json"), + ); + expect(configPkgJson.name).toBe(configPackageName(projectName)); + expect(configPkgJson.private).toBe(true); + + // 3. Config tsconfig.base.json + const configTsConfigBase = await readJSON( + join(projectDir, "packages/config/tsconfig.base.json"), + ); + expect(configTsConfigBase.compilerOptions).toBeDefined(); + expect(configTsConfigBase.compilerOptions.strict).toBe(true); + + // Check runtime-specific types + if (config.runtime === "node") { + expect(configTsConfigBase.compilerOptions.types).toContain("node"); + } else if (config.runtime === "bun") { + expect(configTsConfigBase.compilerOptions.types).toContain("bun"); + } + + // 4. Config tsconfig.json + expect( + await pathExists(join(projectDir, "packages/config/tsconfig.json")), + ).toBe(true); + + // 5. Root configuration + expect(await pathExists(join(projectDir, "tsconfig.base.json"))).toBe(false); + + const rootTsConfig = await readFile(join(projectDir, "tsconfig.json"), "utf-8"); + expect(rootTsConfig).toContain(configTsConfigReference(projectName)); + + const rootPkgJson = await readJSON(join(projectDir, "package.json")); + expect(rootPkgJson.devDependencies[configPackageName(projectName)]).toBe( + "workspace:*", + ); + + // 6. Workspace packages based on config + const shouldHaveDb = + config.database && config.database !== "none" && config.orm !== "none"; + const shouldHaveApi = config.api && config.api !== "none"; + const shouldHaveAuth = config.auth && config.auth !== "none"; + const shouldHaveServer = + config.backend && !["none", "convex", "self"].includes(config.backend); + + if (shouldHaveDb) { + const dbTsConfig = await readFile( + join(projectDir, "packages/db/tsconfig.json"), + "utf-8", + ); + expect(dbTsConfig).toContain(configTsConfigReference(projectName)); + + const dbPkgJson = await readJSON( + join(projectDir, "packages/db/package.json"), + ); + expect(dbPkgJson.devDependencies[configPackageName(projectName)]).toBe( + "workspace:*", + ); + } + + if (shouldHaveApi) { + const apiTsConfig = await readFile( + join(projectDir, "packages/api/tsconfig.json"), + "utf-8", + ); + expect(apiTsConfig).toContain(configTsConfigReference(projectName)); + + const apiPkgJson = await readJSON( + join(projectDir, "packages/api/package.json"), + ); + expect(apiPkgJson.devDependencies[configPackageName(projectName)]).toBe( + "workspace:*", + ); + } + + if (shouldHaveAuth) { + const authTsConfig = await readFile( + join(projectDir, "packages/auth/tsconfig.json"), + "utf-8", + ); + expect(authTsConfig).toContain(configTsConfigReference(projectName)); + + const authPkgJson = await readJSON( + join(projectDir, "packages/auth/package.json"), + ); + expect(authPkgJson.devDependencies[configPackageName(projectName)]).toBe( + "workspace:*", + ); + } + + if (shouldHaveServer) { + const serverTsConfig = await readFile( + join(projectDir, "apps/server/tsconfig.json"), + "utf-8", + ); + expect(serverTsConfig).toContain(configTsConfigReference(projectName)); + + const serverPkgJson = await readJSON( + join(projectDir, "apps/server/package.json"), + ); + expect(serverPkgJson.devDependencies[configPackageName(projectName)]).toBe( + "workspace:*", + ); + } +} + export function expectError(result: TestResult, expectedMessage?: string) { expect(result.success).toBe(false); if (expectedMessage) { From 629fab725cb793992b2b1d7d3451b92c647bc9aa Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 6 Nov 2025 22:14:21 +0000 Subject: [PATCH 06/20] feat: tests --- apps/cli/test/config-package.test.ts | 622 ++++----------------------- 1 file changed, 94 insertions(+), 528 deletions(-) diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index 27ce01d5..fa329970 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -1,14 +1,10 @@ -import { join } from "node:path"; -import { pathExists, readFile, readJSON } from "fs-extra"; -import { afterAll, describe, expect, it } from "vitest"; +import { afterAll, describe, it } from "vitest"; import { cleanupSmokeDirectory, - configPackageName, - configTsConfigReference, expectSuccessWithProjectDir, runTRPCTest, - validateConfigPackageSetup, type TestConfig, + validateConfigPackageSetup, } from "./test-utils"; describe("Config Package Feature", () => { @@ -16,431 +12,160 @@ describe("Config Package Feature", () => { await cleanupSmokeDirectory(); }); - describe("Config Package Structure", () => { - it("should create config package at packages/config", async () => { - const result = await runTRPCTest({ - projectName: "config-pkg-structure", + describe("Basic Stack Configurations", () => { + it("should validate hono + node stack", async () => { + const projectName = "hono-node"; + const config: TestConfig = { + projectName, backend: "hono", runtime: "node", database: "sqlite", orm: "drizzle", + api: "trpc", frontend: ["tanstack-router"], install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - expect(await pathExists(join(projectDir, "packages/config"))).toBe(true); - }); - - it("should NOT create tsconfig.base.json at root", async () => { - const result = await runTRPCTest({ - projectName: "no-root-tsconfig", - backend: "hono", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - expect(await pathExists(join(projectDir, "tsconfig.base.json"))).toBe( - false, - ); - }); - - it("should create tsconfig.json at root that extends config package", async () => { - const projectName = "root-tsconfig"; - const result = await runTRPCTest({ - projectName, - backend: "hono", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - - expect(await pathExists(join(projectDir, "tsconfig.json"))).toBe(true); - - const content = await readFile(join(projectDir, "tsconfig.json"), "utf-8"); - expect(content).toContain(configTsConfigReference(projectName)); + await validateConfigPackageSetup(projectDir, projectName, config); }); - it("should create package.json in config package", async () => { - const projectName = "config-pkg-json"; - const result = await runTRPCTest({ + it("should validate hono + bun stack", async () => { + const projectName = "hono-bun"; + const config: TestConfig = { projectName, backend: "hono", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - expect( - await pathExists(join(projectDir, "packages/config/package.json")), - ).toBe(true); - - const pkgJson = await readJSON( - join(projectDir, "packages/config/package.json"), - ); - expect(pkgJson.name).toBe(configPackageName(projectName)); - expect(pkgJson.private).toBe(true); - }); - - it("should create tsconfig.base.json in config package", async () => { - const result = await runTRPCTest({ - projectName: "config-tsconfig-base", - backend: "hono", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - expect( - await pathExists(join(projectDir, "packages/config/tsconfig.base.json")), - ).toBe(true); - - const content = await readJSON( - join(projectDir, "packages/config/tsconfig.base.json"), - ); - expect(content.compilerOptions).toBeDefined(); - expect(content.compilerOptions.strict).toBe(true); - expect(content.compilerOptions.target).toBe("ESNext"); - }); - - it("should create tsconfig.json in config package", async () => { - const result = await runTRPCTest({ - projectName: "config-tsconfig", - backend: "hono", - runtime: "node", + runtime: "bun", + database: "sqlite", + orm: "drizzle", + api: "trpc", frontend: ["tanstack-router"], install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - - expect( - await pathExists(join(projectDir, "packages/config/tsconfig.json")), - ).toBe(true); + await validateConfigPackageSetup(projectDir, projectName, config); }); - }); - describe("Root Configuration", () => { - it("should include config package in root package.json devDependencies", async () => { - const projectName = "root-config-dep"; - const result = await runTRPCTest({ + it("should validate express + node stack", async () => { + const projectName = "express-node"; + const config: TestConfig = { projectName, - backend: "hono", + backend: "express", runtime: "node", + database: "sqlite", + orm: "drizzle", frontend: ["tanstack-router"], - packageManager: "pnpm", install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - const pkgJson = await readJSON(join(projectDir, "package.json")); - - expect(pkgJson.devDependencies).toBeDefined(); - expect(pkgJson.devDependencies[configPackageName(projectName)]).toBe( - "workspace:*", - ); + await validateConfigPackageSetup(projectDir, projectName, config); }); - it("should use workspace:* for pnpm package manager", async () => { - const projectName = "pnpm-workspace"; - const result = await runTRPCTest({ + it("should validate fastify + node stack", async () => { + const projectName = "fastify-node"; + const config: TestConfig = { projectName, - backend: "hono", + backend: "fastify", runtime: "node", frontend: ["tanstack-router"], - packageManager: "pnpm", install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - const pkgJson = await readJSON(join(projectDir, "package.json")); - - expect(pkgJson.devDependencies[configPackageName(projectName)]).toBe( - "workspace:*", - ); + await validateConfigPackageSetup(projectDir, projectName, config); }); }); - describe("Workspace Package References", () => { - it("should configure db package to extend config package", async () => { - const projectName = "db-config-ref"; - const result = await runTRPCTest({ + describe("Full Stack with Authentication", () => { + it("should validate full stack with better-auth", async () => { + const projectName = "full-stack-auth"; + const config: TestConfig = { projectName, backend: "hono", runtime: "node", database: "postgres", orm: "drizzle", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - expect( - await pathExists(join(projectDir, "packages/db/tsconfig.json")), - ).toBe(true); - - const content = await readFile( - join(projectDir, "packages/db/tsconfig.json"), - "utf-8", - ); - expect(content).toContain(configTsConfigReference(projectName)); - }); - - it("should configure api package to extend config package", async () => { - const projectName = "api-config-ref"; - const result = await runTRPCTest({ - projectName, - backend: "hono", - runtime: "node", api: "trpc", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - expect( - await pathExists(join(projectDir, "packages/api/tsconfig.json")), - ).toBe(true); - - const content = await readFile( - join(projectDir, "packages/api/tsconfig.json"), - "utf-8", - ); - expect(content).toContain(configTsConfigReference(projectName)); - }); - - it("should configure server app to extend config package", async () => { - const projectName = "server-config-ref"; - const result = await runTRPCTest({ - projectName, - backend: "hono", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - expect( - await pathExists(join(projectDir, "apps/server/tsconfig.json")), - ).toBe(true); - - const content = await readFile( - join(projectDir, "apps/server/tsconfig.json"), - "utf-8", - ); - expect(content).toContain(configTsConfigReference(projectName)); - }); - - it("should configure auth package to extend config package", async () => { - const projectName = "auth-config-ref"; - const result = await runTRPCTest({ - projectName, - backend: "hono", - runtime: "node", - database: "postgres", - orm: "drizzle", auth: "better-auth", frontend: ["tanstack-router"], + addons: ["turborepo"], install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - - expect( - await pathExists(join(projectDir, "packages/auth/tsconfig.json")), - ).toBe(true); - - const content = await readFile( - join(projectDir, "packages/auth/tsconfig.json"), - "utf-8", - ); - expect(content).toContain(configTsConfigReference(projectName)); + await validateConfigPackageSetup(projectDir, projectName, config); }); }); - describe("Package Dependencies", () => { - it("should add config package to db package devDependencies", async () => { - const projectName = "db-dep"; - const result = await runTRPCTest({ + describe("API Variants", () => { + it("should validate stack with tRPC", async () => { + const projectName = "trpc-api"; + const config: TestConfig = { projectName, backend: "hono", runtime: "node", + api: "trpc", database: "sqlite", orm: "drizzle", frontend: ["tanstack-router"], install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - const dbPkgJson = await readJSON( - join(projectDir, "packages/db/package.json"), - ); - - expect(dbPkgJson.devDependencies).toBeDefined(); - expect(dbPkgJson.devDependencies[configPackageName(projectName)]).toBe( - "workspace:*", - ); - }); - - it("should add config package to api package devDependencies", async () => { - const projectName = "api-dep"; - const result = await runTRPCTest({ - projectName, - backend: "hono", - runtime: "node", - api: "trpc", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - const apiPkgJson = await readJSON( - join(projectDir, "packages/api/package.json"), - ); - - expect(apiPkgJson.devDependencies).toBeDefined(); - expect(apiPkgJson.devDependencies[configPackageName(projectName)]).toBe( - "workspace:*", - ); - }); - - it("should add config package to server app devDependencies", async () => { - const projectName = "server-dep"; - const result = await runTRPCTest({ - projectName, - backend: "hono", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - const serverPkgJson = await readJSON( - join(projectDir, "apps/server/package.json"), - ); - - expect(serverPkgJson.devDependencies).toBeDefined(); - expect(serverPkgJson.devDependencies[configPackageName(projectName)]).toBe( - "workspace:*", - ); + await validateConfigPackageSetup(projectDir, projectName, config); }); - it("should add config package to auth package devDependencies", async () => { - const projectName = "auth-dep"; - const result = await runTRPCTest({ + it("should validate stack with oRPC", async () => { + const projectName = "orpc-api"; + const config: TestConfig = { projectName, backend: "hono", runtime: "node", - database: "postgres", + api: "orpc", + database: "sqlite", orm: "drizzle", - auth: "better-auth", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - const authPkgJson = await readJSON( - join(projectDir, "packages/auth/package.json"), - ); - - expect(authPkgJson.devDependencies).toBeDefined(); - expect(authPkgJson.devDependencies[configPackageName(projectName)]).toBe( - "workspace:*", - ); - }); - }); - - describe("Runtime-Specific Configuration", () => { - it("should include node types for node runtime", async () => { - const result = await runTRPCTest({ - projectName: "node-runtime", - backend: "hono", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - const configTsConfigBase = await readJSON( - join(projectDir, "packages/config/tsconfig.base.json"), - ); - - expect(configTsConfigBase.compilerOptions.types).toContain("node"); - }); - - it("should include bun types for bun runtime", async () => { - const result = await runTRPCTest({ - projectName: "bun-runtime", - backend: "hono", - runtime: "bun", frontend: ["tanstack-router"], install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - - const configTsConfigBase = await readJSON( - join(projectDir, "packages/config/tsconfig.base.json"), - ); - - expect(configTsConfigBase.compilerOptions.types).toContain("bun"); + await validateConfigPackageSetup(projectDir, projectName, config); }); }); describe("Edge Cases", () => { - it("should create config package with backend: none", async () => { - const result = await runTRPCTest({ - projectName: "no-backend", + it("should validate frontend-only stack (no backend)", async () => { + const projectName = "frontend-only"; + const config: TestConfig = { + projectName, backend: "none", runtime: "none", database: "none", orm: "none", api: "none", + auth: "none", frontend: ["tanstack-router"], install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - const configPkgPath = join(projectDir, "packages/config"); - expect(await pathExists(configPkgPath)).toBe(true); - }); - - it("should create config package with self-hosted backend", async () => { - const result = await runTRPCTest({ - projectName: "self-backend", - backend: "self", - runtime: "none", - api: "trpc", - database: "sqlite", - orm: "drizzle", - frontend: ["next"], - install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - - const configPkgPath = join(projectDir, "packages/config"); - expect(await pathExists(configPkgPath)).toBe(true); + await validateConfigPackageSetup(projectDir, projectName, config); }); - it("should create config package with convex backend", async () => { - const result = await runTRPCTest({ - projectName: "convex-backend", + it("should validate convex backend", async () => { + const projectName = "convex-backend"; + const config: TestConfig = { + projectName, backend: "convex", runtime: "none", database: "none", @@ -448,224 +173,65 @@ describe("Config Package Feature", () => { api: "none", frontend: ["next"], install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - const configPkgPath = join(projectDir, "packages/config"); - expect(await pathExists(configPkgPath)).toBe(true); - }); - - it("should work with different APIs", async () => { - const projectName = "orpc-api"; - const result = await runTRPCTest({ - projectName, - backend: "hono", - runtime: "node", - api: "orpc", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - const content = await readFile( - join(projectDir, "packages/api/tsconfig.json"), - "utf-8", - ); - expect(content).toContain(configTsConfigReference(projectName)); - }); - - it("should work with turborepo addon", async () => { - const result = await runTRPCTest({ - projectName: "with-turborepo", - backend: "hono", - runtime: "node", - frontend: ["tanstack-router"], - addons: ["turborepo"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - const configPkgPath = join(projectDir, "packages/config"); - expect(await pathExists(configPkgPath)).toBe(true); - - const turboJson = join(projectDir, "turbo.json"); - expect(await pathExists(turboJson)).toBe(true); - }); - }); - - describe("Cross-Stack Compatibility", () => { - it("should work with full stack (hono + trpc + drizzle + better-auth)", async () => { - const projectName = "full-stack"; - const result = await runTRPCTest({ - projectName, - backend: "hono", - runtime: "node", - database: "postgres", - orm: "drizzle", - api: "trpc", - auth: "better-auth", - frontend: ["tanstack-router"], - addons: ["turborepo"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - const packages = [ - "packages/config", - "packages/db", - "packages/api", - "packages/auth", - ]; - - for (const pkg of packages) { - expect(await pathExists(join(projectDir, pkg))).toBe(true); - } - - const dbTsConfig = await readFile( - join(projectDir, "packages/db/tsconfig.json"), - "utf-8", - ); - expect(dbTsConfig).toContain(configTsConfigReference(projectName)); - }); - - it("should work with express backend", async () => { - const projectName = "express-stack"; - const result = await runTRPCTest({ - projectName, - backend: "express", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); - - const projectDir = expectSuccessWithProjectDir(result); - - const serverTsConfig = await readFile( - join(projectDir, "apps/server/tsconfig.json"), - "utf-8", - ); - expect(serverTsConfig).toContain(configTsConfigReference(projectName)); - }); - - it("should work with fastify backend", async () => { - const projectName = "fastify-stack"; - const result = await runTRPCTest({ - projectName, - backend: "fastify", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }); + }; + const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - - const serverTsConfig = await readFile( - join(projectDir, "apps/server/tsconfig.json"), - "utf-8", - ); - expect(serverTsConfig).toContain(configTsConfigReference(projectName)); + await validateConfigPackageSetup(projectDir, projectName, config); }); - }); - describe("Comprehensive Config Validation (Using Helper)", () => { - it("should validate basic stack configuration", async () => { - const projectName = "basic-stack"; + it("should validate self-hosted backend", async () => { + const projectName = "self-backend"; const config: TestConfig = { projectName, - backend: "hono", - runtime: "node", + backend: "self", + runtime: "none", + api: "trpc", database: "sqlite", orm: "drizzle", - api: "trpc", - frontend: ["tanstack-router"], + frontend: ["next"], install: false, }; const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); }); - it("should validate full stack with auth", async () => { - const projectName = "full-stack-helper"; + it("should validate stack without database", async () => { + const projectName = "no-database"; const config: TestConfig = { projectName, backend: "hono", - runtime: "bun", - database: "postgres", - orm: "drizzle", + runtime: "node", + database: "none", + orm: "none", api: "trpc", - auth: "better-auth", frontend: ["tanstack-router"], - addons: ["turborepo"], install: false, }; const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); }); - it("should validate minimal frontend-only stack", async () => { - const projectName = "frontend-only"; + it("should validate stack with turborepo addon", async () => { + const projectName = "with-turborepo"; const config: TestConfig = { projectName, - backend: "none", - runtime: "none", - database: "none", - orm: "none", - api: "none", - auth: "none", + backend: "hono", + runtime: "node", + database: "sqlite", + orm: "drizzle", frontend: ["tanstack-router"], + addons: ["turborepo"], install: false, }; const result = await runTRPCTest(config); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); }); - - it("should validate different backends and runtimes", async () => { - const configs: TestConfig[] = [ - { - projectName: "express-node", - backend: "express", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }, - { - projectName: "hono-bun", - backend: "hono", - runtime: "bun", - frontend: ["tanstack-router"], - install: false, - }, - { - projectName: "fastify-node", - backend: "fastify", - runtime: "node", - frontend: ["tanstack-router"], - install: false, - }, - ]; - - for (const config of configs) { - const result = await runTRPCTest(config); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup( - projectDir, - config.projectName as string, - config, - ); - } - }); }); }); From f591de50b5d726a916bfe480dfd530edb00aef8c Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 6 Nov 2025 22:21:11 +0000 Subject: [PATCH 07/20] feat: tests --- apps/cli/test/config-package.test.ts | 108 ++++++++++++--------------- apps/cli/test/test-utils.ts | 8 +- 2 files changed, 54 insertions(+), 62 deletions(-) diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index fa329970..11e9530b 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -3,7 +3,6 @@ import { cleanupSmokeDirectory, expectSuccessWithProjectDir, runTRPCTest, - type TestConfig, validateConfigPackageSetup, } from "./test-utils"; @@ -15,7 +14,7 @@ describe("Config Package Feature", () => { describe("Basic Stack Configurations", () => { it("should validate hono + node stack", async () => { const projectName = "hono-node"; - const config: TestConfig = { + const result = await runTRPCTest({ projectName, backend: "hono", runtime: "node", @@ -24,16 +23,15 @@ describe("Config Package Feature", () => { api: "trpc", frontend: ["tanstack-router"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); it("should validate hono + bun stack", async () => { const projectName = "hono-bun"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "hono", runtime: "bun", @@ -42,16 +40,15 @@ describe("Config Package Feature", () => { api: "trpc", frontend: ["tanstack-router"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); it("should validate express + node stack", async () => { const projectName = "express-node"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "express", runtime: "node", @@ -59,33 +56,31 @@ describe("Config Package Feature", () => { orm: "drizzle", frontend: ["tanstack-router"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); it("should validate fastify + node stack", async () => { const projectName = "fastify-node"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "fastify", runtime: "node", frontend: ["tanstack-router"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); }); describe("Full Stack with Authentication", () => { it("should validate full stack with better-auth", async () => { const projectName = "full-stack-auth"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "hono", runtime: "node", @@ -96,18 +91,17 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], addons: ["turborepo"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); }); describe("API Variants", () => { it("should validate stack with tRPC", async () => { const projectName = "trpc-api"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "hono", runtime: "node", @@ -116,16 +110,15 @@ describe("Config Package Feature", () => { orm: "drizzle", frontend: ["tanstack-router"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); it("should validate stack with oRPC", async () => { const projectName = "orpc-api"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "hono", runtime: "node", @@ -134,18 +127,17 @@ describe("Config Package Feature", () => { orm: "drizzle", frontend: ["tanstack-router"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); }); describe("Edge Cases", () => { it("should validate frontend-only stack (no backend)", async () => { const projectName = "frontend-only"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "none", runtime: "none", @@ -155,16 +147,15 @@ describe("Config Package Feature", () => { auth: "none", frontend: ["tanstack-router"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); it("should validate convex backend", async () => { const projectName = "convex-backend"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "convex", runtime: "none", @@ -173,16 +164,15 @@ describe("Config Package Feature", () => { api: "none", frontend: ["next"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); it("should validate self-hosted backend", async () => { const projectName = "self-backend"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "self", runtime: "none", @@ -191,16 +181,15 @@ describe("Config Package Feature", () => { orm: "drizzle", frontend: ["next"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); it("should validate stack without database", async () => { const projectName = "no-database"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "hono", runtime: "node", @@ -209,16 +198,15 @@ describe("Config Package Feature", () => { api: "trpc", frontend: ["tanstack-router"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); it("should validate stack with turborepo addon", async () => { const projectName = "with-turborepo"; - const config: TestConfig = { + + const result = await runTRPCTest({ projectName, backend: "hono", runtime: "node", @@ -227,11 +215,9 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], addons: ["turborepo"], install: false, - }; - - const result = await runTRPCTest(config); + }); const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, config); + await validateConfigPackageSetup(projectDir, projectName, result.config); }); }); }); diff --git a/apps/cli/test/test-utils.ts b/apps/cli/test/test-utils.ts index a10e80cb..da57ec5e 100644 --- a/apps/cli/test/test-utils.ts +++ b/apps/cli/test/test-utils.ts @@ -42,6 +42,7 @@ export interface TestResult { result?: InitResult; error?: string; projectDir?: string; + config: TestConfig; } export interface TestConfig extends CreateInput { @@ -143,11 +144,13 @@ export async function runTRPCTest(config: TestConfig): Promise { result: result?.success ? result : undefined, error: result?.success ? undefined : result?.error, projectDir: result?.success ? result?.projectDirectory : undefined, + config, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error), + config, }; } finally { // Always restore original environment @@ -233,7 +236,10 @@ export async function validateConfigPackageSetup( // 5. Root configuration expect(await pathExists(join(projectDir, "tsconfig.base.json"))).toBe(false); - const rootTsConfig = await readFile(join(projectDir, "tsconfig.json"), "utf-8"); + const rootTsConfig = await readFile( + join(projectDir, "tsconfig.json"), + "utf-8", + ); expect(rootTsConfig).toContain(configTsConfigReference(projectName)); const rootPkgJson = await readJSON(join(projectDir, "package.json")); From 66b5aa875f784372dd7e54f9457c828fff209894 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 6 Nov 2025 22:25:50 +0000 Subject: [PATCH 08/20] feat: tests --- apps/cli/test/config-package.test.ts | 84 ++++++++-------------------- apps/cli/test/test-utils.ts | 9 ++- 2 files changed, 30 insertions(+), 63 deletions(-) diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index 11e9530b..c321a3c9 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -1,7 +1,6 @@ import { afterAll, describe, it } from "vitest"; import { cleanupSmokeDirectory, - expectSuccessWithProjectDir, runTRPCTest, validateConfigPackageSetup, } from "./test-utils"; @@ -13,9 +12,8 @@ describe("Config Package Feature", () => { describe("Basic Stack Configurations", () => { it("should validate hono + node stack", async () => { - const projectName = "hono-node"; const result = await runTRPCTest({ - projectName, + projectName: "hono-node", backend: "hono", runtime: "node", database: "sqlite", @@ -24,15 +22,12 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); it("should validate hono + bun stack", async () => { - const projectName = "hono-bun"; - const result = await runTRPCTest({ - projectName, + projectName: "hono-bun", backend: "hono", runtime: "bun", database: "sqlite", @@ -41,15 +36,12 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); it("should validate express + node stack", async () => { - const projectName = "express-node"; - const result = await runTRPCTest({ - projectName, + projectName: "express-node", backend: "express", runtime: "node", database: "sqlite", @@ -57,31 +49,25 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); it("should validate fastify + node stack", async () => { - const projectName = "fastify-node"; - const result = await runTRPCTest({ - projectName, + projectName: "fastify-node", backend: "fastify", runtime: "node", frontend: ["tanstack-router"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); }); describe("Full Stack with Authentication", () => { it("should validate full stack with better-auth", async () => { - const projectName = "full-stack-auth"; - const result = await runTRPCTest({ - projectName, + projectName: "full-stack-auth", backend: "hono", runtime: "node", database: "postgres", @@ -92,17 +78,14 @@ describe("Config Package Feature", () => { addons: ["turborepo"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); }); describe("API Variants", () => { it("should validate stack with tRPC", async () => { - const projectName = "trpc-api"; - const result = await runTRPCTest({ - projectName, + projectName: "trpc-api", backend: "hono", runtime: "node", api: "trpc", @@ -111,15 +94,12 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); it("should validate stack with oRPC", async () => { - const projectName = "orpc-api"; - const result = await runTRPCTest({ - projectName, + projectName: "orpc-api", backend: "hono", runtime: "node", api: "orpc", @@ -128,17 +108,14 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); }); describe("Edge Cases", () => { it("should validate frontend-only stack (no backend)", async () => { - const projectName = "frontend-only"; - const result = await runTRPCTest({ - projectName, + projectName: "frontend-only", backend: "none", runtime: "none", database: "none", @@ -148,15 +125,12 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); it("should validate convex backend", async () => { - const projectName = "convex-backend"; - const result = await runTRPCTest({ - projectName, + projectName: "convex-backend", backend: "convex", runtime: "none", database: "none", @@ -165,15 +139,12 @@ describe("Config Package Feature", () => { frontend: ["next"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); it("should validate self-hosted backend", async () => { - const projectName = "self-backend"; - const result = await runTRPCTest({ - projectName, + projectName: "self-backend", backend: "self", runtime: "none", api: "trpc", @@ -182,15 +153,12 @@ describe("Config Package Feature", () => { frontend: ["next"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); it("should validate stack without database", async () => { - const projectName = "no-database"; - const result = await runTRPCTest({ - projectName, + projectName: "no-database", backend: "hono", runtime: "node", database: "none", @@ -199,15 +167,12 @@ describe("Config Package Feature", () => { frontend: ["tanstack-router"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); it("should validate stack with turborepo addon", async () => { - const projectName = "with-turborepo"; - const result = await runTRPCTest({ - projectName, + projectName: "with-turborepo", backend: "hono", runtime: "node", database: "sqlite", @@ -216,8 +181,7 @@ describe("Config Package Feature", () => { addons: ["turborepo"], install: false, }); - const projectDir = expectSuccessWithProjectDir(result); - await validateConfigPackageSetup(projectDir, projectName, result.config); + await validateConfigPackageSetup(result); }); }); }); diff --git a/apps/cli/test/test-utils.ts b/apps/cli/test/test-utils.ts index da57ec5e..bd37bfe0 100644 --- a/apps/cli/test/test-utils.ts +++ b/apps/cli/test/test-utils.ts @@ -196,14 +196,17 @@ export function configTsConfigReference(projectName: string): string { * Validates all expected files and references based on the CreateInput configuration */ export async function validateConfigPackageSetup( - projectDir: string, - projectName: string, - config: CreateInput, + result: TestResult, ): Promise { const { pathExists, readFile, readJSON } = await import("fs-extra"); const { join } = await import("node:path"); const { expect } = await import("vitest"); + // Extract data from result + const projectDir = expectSuccessWithProjectDir(result); + const config = result.config; + const projectName = config.projectName as string; + // 1. Config package structure expect(await pathExists(join(projectDir, "packages/config"))).toBe(true); From e753bc96d13aa3e20df643554487f135f3089597 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 19:54:16 +0000 Subject: [PATCH 09/20] feat: rollback devDependencies --- apps/cli/templates/api/orpc/server/package.json.hbs | 4 +--- apps/cli/templates/api/trpc/server/package.json.hbs | 4 +--- .../templates/auth/better-auth/server/base/package.json.hbs | 4 +--- apps/cli/templates/backend/server/base/package.json.hbs | 1 - apps/cli/templates/base/package.json.hbs | 4 +--- apps/cli/templates/db/base/package.json.hbs | 4 +--- 6 files changed, 5 insertions(+), 16 deletions(-) diff --git a/apps/cli/templates/api/orpc/server/package.json.hbs b/apps/cli/templates/api/orpc/server/package.json.hbs index d919922b..f85df04d 100644 --- a/apps/cli/templates/api/orpc/server/package.json.hbs +++ b/apps/cli/templates/api/orpc/server/package.json.hbs @@ -14,9 +14,7 @@ "scripts": { "build": "tsdown" }, - "devDependencies": { - "@{{projectName}}/config": "workspace:*" - }, + "devDependencies": {}, "peerDependencies": { "typescript": "^5" }, diff --git a/apps/cli/templates/api/trpc/server/package.json.hbs b/apps/cli/templates/api/trpc/server/package.json.hbs index feaab140..003865be 100644 --- a/apps/cli/templates/api/trpc/server/package.json.hbs +++ b/apps/cli/templates/api/trpc/server/package.json.hbs @@ -14,9 +14,7 @@ "scripts": { "build": "tsdown" }, - "devDependencies": { - "@{{projectName}}/config": "workspace:*" - }, + "devDependencies": {}, "peerDependencies": { "typescript": "^5" } diff --git a/apps/cli/templates/auth/better-auth/server/base/package.json.hbs b/apps/cli/templates/auth/better-auth/server/base/package.json.hbs index 2802b21f..f194f64e 100644 --- a/apps/cli/templates/auth/better-auth/server/base/package.json.hbs +++ b/apps/cli/templates/auth/better-auth/server/base/package.json.hbs @@ -14,9 +14,7 @@ "scripts": { "build": "tsdown" }, - "devDependencies": { - "@{{projectName}}/config": "workspace:*" - }, + "devDependencies": {}, "peerDependencies": { "typescript": "^5" } diff --git a/apps/cli/templates/backend/server/base/package.json.hbs b/apps/cli/templates/backend/server/base/package.json.hbs index 6ad1b821..e3957602 100644 --- a/apps/cli/templates/backend/server/base/package.json.hbs +++ b/apps/cli/templates/backend/server/base/package.json.hbs @@ -14,7 +14,6 @@ ], {{/if}} "devDependencies": { - "@{{projectName}}/config": "workspace:*", "typescript": "^5.8.2" } } diff --git a/apps/cli/templates/base/package.json.hbs b/apps/cli/templates/base/package.json.hbs index 2a46af37..fc482afa 100644 --- a/apps/cli/templates/base/package.json.hbs +++ b/apps/cli/templates/base/package.json.hbs @@ -7,7 +7,5 @@ "packages/*" ], "scripts": {}, - "devDependencies": { - "@{{projectName}}/config": "workspace:*" - } + "devDependencies": {} } diff --git a/apps/cli/templates/db/base/package.json.hbs b/apps/cli/templates/db/base/package.json.hbs index 12f6566c..342cae89 100644 --- a/apps/cli/templates/db/base/package.json.hbs +++ b/apps/cli/templates/db/base/package.json.hbs @@ -14,9 +14,7 @@ "scripts": { "build": "tsdown" }, - "devDependencies": { - "@{{projectName}}/config": "workspace:*" - }, + "devDependencies": {}, "peerDependencies": { "typescript": "^5" } From 9edd4d79041f3b3f945da528b598375f0fffc348 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 19:54:26 +0000 Subject: [PATCH 10/20] feat: ai --- apps/cli/src/helpers/core/workspace-setup.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/cli/src/helpers/core/workspace-setup.ts b/apps/cli/src/helpers/core/workspace-setup.ts index 34786bc5..21cdd684 100644 --- a/apps/cli/src/helpers/core/workspace-setup.ts +++ b/apps/cli/src/helpers/core/workspace-setup.ts @@ -15,11 +15,17 @@ export async function setupWorkspaceDependencies( const commonDeps: AvailableDependencies[] = ["dotenv", "zod"]; const commonDevDeps: AvailableDependencies[] = ["tsdown"]; + // Config package should be a devDependency for all workspace packages + const configPackageDep: Record = { + [`@${projectName}/config`]: workspaceVersion, + }; + const dbPackageDir = path.join(projectDir, "packages/db"); if (await fs.pathExists(dbPackageDir)) { await addPackageDependency({ dependencies: commonDeps, devDependencies: commonDevDeps, + customDevDependencies: configPackageDep, projectDir: dbPackageDir, }); } @@ -35,6 +41,7 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: authDeps, + customDevDependencies: configPackageDep, projectDir: authPackageDir, }); } @@ -53,6 +60,7 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: apiDeps, + customDevDependencies: configPackageDep, projectDir: apiPackageDir, }); } @@ -74,6 +82,7 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: serverDeps, + customDevDependencies: configPackageDep, projectDir: serverPackageDir, }); } @@ -118,6 +127,7 @@ export async function setupWorkspaceDependencies( await addPackageDependency({ dependencies: commonDeps, devDependencies: [...commonDevDeps, ...runtimeDevDeps], + customDevDependencies: configPackageDep, projectDir, }); } From 35c3b567b50e1c36093d064b162736fb359df60f Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 19:54:36 +0000 Subject: [PATCH 11/20] Revert "feat: ai" This reverts commit eef2c90e4280dcf6561f48038cdf5d69560690fc. --- apps/cli/src/helpers/core/workspace-setup.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/apps/cli/src/helpers/core/workspace-setup.ts b/apps/cli/src/helpers/core/workspace-setup.ts index 21cdd684..34786bc5 100644 --- a/apps/cli/src/helpers/core/workspace-setup.ts +++ b/apps/cli/src/helpers/core/workspace-setup.ts @@ -15,17 +15,11 @@ export async function setupWorkspaceDependencies( const commonDeps: AvailableDependencies[] = ["dotenv", "zod"]; const commonDevDeps: AvailableDependencies[] = ["tsdown"]; - // Config package should be a devDependency for all workspace packages - const configPackageDep: Record = { - [`@${projectName}/config`]: workspaceVersion, - }; - const dbPackageDir = path.join(projectDir, "packages/db"); if (await fs.pathExists(dbPackageDir)) { await addPackageDependency({ dependencies: commonDeps, devDependencies: commonDevDeps, - customDevDependencies: configPackageDep, projectDir: dbPackageDir, }); } @@ -41,7 +35,6 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: authDeps, - customDevDependencies: configPackageDep, projectDir: authPackageDir, }); } @@ -60,7 +53,6 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: apiDeps, - customDevDependencies: configPackageDep, projectDir: apiPackageDir, }); } @@ -82,7 +74,6 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: serverDeps, - customDevDependencies: configPackageDep, projectDir: serverPackageDir, }); } @@ -127,7 +118,6 @@ export async function setupWorkspaceDependencies( await addPackageDependency({ dependencies: commonDeps, devDependencies: [...commonDevDeps, ...runtimeDevDeps], - customDevDependencies: configPackageDep, projectDir, }); } From 84ce7d21c416df8056cf1354eaf016d8eb73f855 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 21:33:12 +0000 Subject: [PATCH 12/20] feat: config --- apps/cli/src/helpers/core/project-config.ts | 7 +++++++ apps/cli/src/helpers/core/workspace-setup.ts | 13 ++++++++++++ apps/cli/test/config-package.test.ts | 21 +++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/cli/src/helpers/core/project-config.ts b/apps/cli/src/helpers/core/project-config.ts index 11a2f5c0..dbf03907 100644 --- a/apps/cli/src/helpers/core/project-config.ts +++ b/apps/cli/src/helpers/core/project-config.ts @@ -243,6 +243,13 @@ async function updateRootPackageJson( } } + const configPackageDir = path.join(projectDir, "packages/config"); + if (await fs.pathExists(configPackageDir)) { + packageJson.devDependencies = { + [`@${options.projectName}/config`]: "workspace:*", + }; + } + await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 }); } diff --git a/apps/cli/src/helpers/core/workspace-setup.ts b/apps/cli/src/helpers/core/workspace-setup.ts index 34786bc5..a5cf3384 100644 --- a/apps/cli/src/helpers/core/workspace-setup.ts +++ b/apps/cli/src/helpers/core/workspace-setup.ts @@ -15,11 +15,19 @@ export async function setupWorkspaceDependencies( const commonDeps: AvailableDependencies[] = ["dotenv", "zod"]; const commonDevDeps: AvailableDependencies[] = ["tsdown"]; + const configPackageDir = path.join(projectDir, "packages/config"); + const configDep: Record = {}; + + if (await fs.pathExists(configPackageDir)) { + configDep[`@${projectName}/config`] = workspaceVersion; + } + const dbPackageDir = path.join(projectDir, "packages/db"); if (await fs.pathExists(dbPackageDir)) { await addPackageDependency({ dependencies: commonDeps, devDependencies: commonDevDeps, + customDevDependencies: configDep, projectDir: dbPackageDir, }); } @@ -35,6 +43,7 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: authDeps, + customDevDependencies: configDep, projectDir: authPackageDir, }); } @@ -53,6 +62,7 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: apiDeps, + customDevDependencies: configDep, projectDir: apiPackageDir, }); } @@ -74,6 +84,7 @@ export async function setupWorkspaceDependencies( dependencies: commonDeps, devDependencies: commonDevDeps, customDependencies: serverDeps, + customDevDependencies: configDep, projectDir: serverPackageDir, }); } @@ -92,6 +103,7 @@ export async function setupWorkspaceDependencies( if (Object.keys(webDeps).length > 0) { await addPackageDependency({ customDependencies: webDeps, + customDevDependencies: configDep, projectDir: webPackageDir, }); } @@ -108,6 +120,7 @@ export async function setupWorkspaceDependencies( if (Object.keys(nativeDeps).length > 0) { await addPackageDependency({ customDependencies: nativeDeps, + customDevDependencies: configDep, projectDir: nativePackageDir, }); } diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index c321a3c9..e7d7e04d 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -1,4 +1,4 @@ -import { afterAll, describe, it } from "vitest"; +import { afterAll, beforeAll, describe, it } from "vitest"; import { cleanupSmokeDirectory, runTRPCTest, @@ -6,11 +6,30 @@ import { } from "./test-utils"; describe("Config Package Feature", () => { + beforeAll(async () => { + await cleanupSmokeDirectory(); + }); afterAll(async () => { await cleanupSmokeDirectory(); }); describe("Basic Stack Configurations", () => { + it("should validate hono + pnpm + turbo stack", async () => { + const result = await runTRPCTest({ + projectName: "hono-pnpm", + backend: "hono", + runtime: "node", + packageManager: "pnpm", + database: "sqlite", + orm: "drizzle", + api: "trpc", + frontend: ["tanstack-router"], + addons: ["turborepo"], + install: false, + }); + await validateConfigPackageSetup(result); + }); + it("should validate hono + node stack", async () => { const result = await runTRPCTest({ projectName: "hono-node", From 0eac7feae066ef97b6269b84567130f564dee267 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 21:46:47 +0000 Subject: [PATCH 13/20] feat: config --- apps/cli/templates/packages/config/tsconfig.base.json.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/cli/templates/packages/config/tsconfig.base.json.hbs b/apps/cli/templates/packages/config/tsconfig.base.json.hbs index a1da881f..caebbeca 100644 --- a/apps/cli/templates/packages/config/tsconfig.base.json.hbs +++ b/apps/cli/templates/packages/config/tsconfig.base.json.hbs @@ -30,4 +30,4 @@ "@cloudflare/workers-types"{{/if}} ] } -} +} \ No newline at end of file From ca0f02eb0bb031c57c722b6270893ae18b525b8c Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 21:53:31 +0000 Subject: [PATCH 14/20] feat: rabbit --- apps/cli/src/helpers/core/project-config.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/cli/src/helpers/core/project-config.ts b/apps/cli/src/helpers/core/project-config.ts index dbf03907..9cf9c6a3 100644 --- a/apps/cli/src/helpers/core/project-config.ts +++ b/apps/cli/src/helpers/core/project-config.ts @@ -35,6 +35,9 @@ async function updateRootPackageJson( const packageJson = await fs.readJson(rootPackageJsonPath); packageJson.name = options.projectName; + const workspaceVersion = + options.packageManager === "npm" ? "*" : "workspace:*"; + if (!packageJson.scripts) { packageJson.scripts = {}; } @@ -245,9 +248,8 @@ async function updateRootPackageJson( const configPackageDir = path.join(projectDir, "packages/config"); if (await fs.pathExists(configPackageDir)) { - packageJson.devDependencies = { - [`@${options.projectName}/config`]: "workspace:*", - }; + packageJson.devDependencies[`@${options.projectName}/config`] = + workspaceVersion; } await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 }); From 90b332a9d977926567d50ab4f4efd6505711c0c1 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 21:58:50 +0000 Subject: [PATCH 15/20] feat: rabbit --- apps/cli/src/helpers/core/project-config.ts | 3 +++ apps/cli/templates/base/package.json.hbs | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/cli/src/helpers/core/project-config.ts b/apps/cli/src/helpers/core/project-config.ts index 9cf9c6a3..171b34ff 100644 --- a/apps/cli/src/helpers/core/project-config.ts +++ b/apps/cli/src/helpers/core/project-config.ts @@ -248,6 +248,9 @@ async function updateRootPackageJson( const configPackageDir = path.join(projectDir, "packages/config"); if (await fs.pathExists(configPackageDir)) { + if (!packageJson.devDependencies) { + packageJson.devDependencies = {}; + } packageJson.devDependencies[`@${options.projectName}/config`] = workspaceVersion; } diff --git a/apps/cli/templates/base/package.json.hbs b/apps/cli/templates/base/package.json.hbs index fc482afa..84d5c799 100644 --- a/apps/cli/templates/base/package.json.hbs +++ b/apps/cli/templates/base/package.json.hbs @@ -6,6 +6,5 @@ "apps/*", "packages/*" ], - "scripts": {}, - "devDependencies": {} + "scripts": {} } From 6e3ad4641579dee933220923a3fbad0df88e9784 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 22:34:12 +0000 Subject: [PATCH 16/20] feat: prune tests --- apps/cli/test/config-package.test.ts | 6 ++- apps/cli/test/test-utils.ts | 73 ++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index e7d7e04d..872074fb 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -3,6 +3,7 @@ import { cleanupSmokeDirectory, runTRPCTest, validateConfigPackageSetup, + validateTurboPrune, } from "./test-utils"; describe("Config Package Feature", () => { @@ -10,7 +11,7 @@ describe("Config Package Feature", () => { await cleanupSmokeDirectory(); }); afterAll(async () => { - await cleanupSmokeDirectory(); + // await cleanupSmokeDirectory(); }); describe("Basic Stack Configurations", () => { @@ -28,6 +29,7 @@ describe("Config Package Feature", () => { install: false, }); await validateConfigPackageSetup(result); + await validateTurboPrune(result); }); it("should validate hono + node stack", async () => { @@ -98,6 +100,7 @@ describe("Config Package Feature", () => { install: false, }); await validateConfigPackageSetup(result); + await validateTurboPrune(result); }); }); @@ -201,6 +204,7 @@ describe("Config Package Feature", () => { install: false, }); await validateConfigPackageSetup(result); + await validateTurboPrune(result); }); }); }); diff --git a/apps/cli/test/test-utils.ts b/apps/cli/test/test-utils.ts index bd37bfe0..6226e11d 100644 --- a/apps/cli/test/test-utils.ts +++ b/apps/cli/test/test-utils.ts @@ -319,6 +319,79 @@ export async function validateConfigPackageSetup( } } +/** + * Validate that turbo prune works correctly for turborepo projects + * Only runs if the project has turborepo addon enabled + * Generates lockfile without installing dependencies, then runs turbo prune + */ +export async function validateTurboPrune(result: TestResult): Promise { + const { expect } = await import("vitest"); + const { execa } = await import("execa"); + + // Extract data from result + const projectDir = expectSuccessWithProjectDir(result); + const config = result.config; + + // Only run this validation if turborepo addon is enabled + const hasTurborepo = + config.addons && + Array.isArray(config.addons) && + config.addons.includes("turborepo"); + + if (!hasTurborepo) { + return; + } + + const packageManager = config.packageManager || "pnpm"; + + // Generate lockfile without installing dependencies + try { + if (packageManager === "pnpm") { + await execa("pnpm", ["install", "--lockfile-only"], { + cwd: projectDir, + }); + } else if (packageManager === "npm") { + await execa("npm", ["install", "--package-lock-only"], { + cwd: projectDir, + }); + } else if (packageManager === "bun") { + // Bun doesn't have --lockfile-only, so we skip for bun + // or use bun install which is fast anyway + await execa("bun", ["install", "--frozen-lockfile"], { + cwd: projectDir, + reject: false, // Don't fail if frozen-lockfile doesn't exist + }); + } + } catch (error) { + console.error("Failed to generate lockfile:"); + console.error(error); + expect.fail( + `Failed to generate lockfile: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + // Determine package manager command for running turbo + const command = + packageManager === "npm" ? "npx" : packageManager === "bun" ? "bunx" : "pnpm"; + const args = + packageManager === "pnpm" + ? ["dlx", "turbo", "prune", "server", "--docker"] + : ["turbo", "prune", "server", "--docker"]; + + // Run turbo prune server --docker + try { + await execa(command, args, { + cwd: projectDir, + }); + } catch (error) { + console.error("turbo prune failed:"); + console.error(error); + expect.fail( + `turbo prune server --docker failed: ${error instanceof Error ? error.message : String(error)}`, + ); + } +} + export function expectError(result: TestResult, expectedMessage?: string) { expect(result.success).toBe(false); if (expectedMessage) { From 751eb2e1bc3a5dc5291a15affdfae0f9f7e49cc9 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 7 Nov 2025 22:35:47 +0000 Subject: [PATCH 17/20] feat: prune tests --- apps/cli/test/test-utils.ts | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/cli/test/test-utils.ts b/apps/cli/test/test-utils.ts index 6226e11d..43c4b8b9 100644 --- a/apps/cli/test/test-utils.ts +++ b/apps/cli/test/test-utils.ts @@ -373,22 +373,27 @@ export async function validateTurboPrune(result: TestResult): Promise { // Determine package manager command for running turbo const command = packageManager === "npm" ? "npx" : packageManager === "bun" ? "bunx" : "pnpm"; - const args = - packageManager === "pnpm" - ? ["dlx", "turbo", "prune", "server", "--docker"] - : ["turbo", "prune", "server", "--docker"]; - // Run turbo prune server --docker - try { - await execa(command, args, { - cwd: projectDir, - }); - } catch (error) { - console.error("turbo prune failed:"); - console.error(error); - expect.fail( - `turbo prune server --docker failed: ${error instanceof Error ? error.message : String(error)}`, - ); + // Test turbo prune for both server and web targets + const targets = ["server", "web"]; + + for (const target of targets) { + const args = + packageManager === "pnpm" + ? ["dlx", "turbo", "prune", target, "--docker"] + : ["turbo", "prune", target, "--docker"]; + + try { + await execa(command, args, { + cwd: projectDir, + }); + } catch (error) { + console.error(`turbo prune ${target} failed:`); + console.error(error); + expect.fail( + `turbo prune ${target} --docker failed: ${error instanceof Error ? error.message : String(error)}`, + ); + } } } From d7ac29abd39e8ce07fac68788fc40a510aa8cea8 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 8 Nov 2025 13:39:10 +0000 Subject: [PATCH 18/20] feat: wrangler tests --- apps/cli/test/config-package.test.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/cli/test/config-package.test.ts b/apps/cli/test/config-package.test.ts index 872074fb..0d6e8249 100644 --- a/apps/cli/test/config-package.test.ts +++ b/apps/cli/test/config-package.test.ts @@ -11,7 +11,7 @@ describe("Config Package Feature", () => { await cleanupSmokeDirectory(); }); afterAll(async () => { - // await cleanupSmokeDirectory(); + await cleanupSmokeDirectory(); }); describe("Basic Stack Configurations", () => { @@ -32,6 +32,25 @@ describe("Config Package Feature", () => { await validateTurboPrune(result); }); + it("should validate hono + pnpm + turbo + wrangler stack", async () => { + const result = await runTRPCTest({ + projectName: "hono-pnpm-turbo-wrangler", + backend: "hono", + runtime: "workers", + packageManager: "pnpm", + database: "sqlite", + orm: "drizzle", + api: "trpc", + frontend: ["tanstack-router"], + addons: ["turborepo"], + serverDeploy: "wrangler", + webDeploy: "wrangler", + install: false, + }); + await validateConfigPackageSetup(result); + await validateTurboPrune(result); + }); + it("should validate hono + node stack", async () => { const result = await runTRPCTest({ projectName: "hono-node", From fcff4a095fe69e42724944aa0d5a9dbac7bca15d Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 8 Nov 2025 13:56:04 +0000 Subject: [PATCH 19/20] feat: tests --- apps/cli/test/test-utils.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/cli/test/test-utils.ts b/apps/cli/test/test-utils.ts index 43c4b8b9..162f483d 100644 --- a/apps/cli/test/test-utils.ts +++ b/apps/cli/test/test-utils.ts @@ -225,7 +225,11 @@ export async function validateConfigPackageSetup( expect(configTsConfigBase.compilerOptions.strict).toBe(true); // Check runtime-specific types - if (config.runtime === "node") { + if ( + config.runtime === "node" || + config.runtime === "workers" || + config.runtime === "none" + ) { expect(configTsConfigBase.compilerOptions.types).toContain("node"); } else if (config.runtime === "bun") { expect(configTsConfigBase.compilerOptions.types).toContain("bun"); @@ -372,7 +376,11 @@ export async function validateTurboPrune(result: TestResult): Promise { // Determine package manager command for running turbo const command = - packageManager === "npm" ? "npx" : packageManager === "bun" ? "bunx" : "pnpm"; + packageManager === "npm" + ? "npx" + : packageManager === "bun" + ? "bunx" + : "pnpm"; // Test turbo prune for both server and web targets const targets = ["server", "web"]; From e3c355e1c5df6045648efa74e36999ba8e99a8b1 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Tue, 11 Nov 2025 01:46:30 +0530 Subject: [PATCH 20/20] fix --- apps/cli/src/helpers/core/project-config.ts | 12 ------------ apps/cli/src/helpers/core/workspace-setup.ts | 1 - apps/cli/templates/base/tsconfig.json.hbs | 3 --- apps/cli/templates/packages/config/package.json.hbs | 3 +-- apps/cli/templates/packages/config/tsconfig.json | 7 ------- 5 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 apps/cli/templates/base/tsconfig.json.hbs delete mode 100644 apps/cli/templates/packages/config/tsconfig.json diff --git a/apps/cli/src/helpers/core/project-config.ts b/apps/cli/src/helpers/core/project-config.ts index 171b34ff..11a2f5c0 100644 --- a/apps/cli/src/helpers/core/project-config.ts +++ b/apps/cli/src/helpers/core/project-config.ts @@ -35,9 +35,6 @@ async function updateRootPackageJson( const packageJson = await fs.readJson(rootPackageJsonPath); packageJson.name = options.projectName; - const workspaceVersion = - options.packageManager === "npm" ? "*" : "workspace:*"; - if (!packageJson.scripts) { packageJson.scripts = {}; } @@ -246,15 +243,6 @@ async function updateRootPackageJson( } } - const configPackageDir = path.join(projectDir, "packages/config"); - if (await fs.pathExists(configPackageDir)) { - if (!packageJson.devDependencies) { - packageJson.devDependencies = {}; - } - packageJson.devDependencies[`@${options.projectName}/config`] = - workspaceVersion; - } - await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 }); } diff --git a/apps/cli/src/helpers/core/workspace-setup.ts b/apps/cli/src/helpers/core/workspace-setup.ts index a5cf3384..bd869172 100644 --- a/apps/cli/src/helpers/core/workspace-setup.ts +++ b/apps/cli/src/helpers/core/workspace-setup.ts @@ -17,7 +17,6 @@ export async function setupWorkspaceDependencies( const configPackageDir = path.join(projectDir, "packages/config"); const configDep: Record = {}; - if (await fs.pathExists(configPackageDir)) { configDep[`@${projectName}/config`] = workspaceVersion; } diff --git a/apps/cli/templates/base/tsconfig.json.hbs b/apps/cli/templates/base/tsconfig.json.hbs deleted file mode 100644 index 8049e75c..00000000 --- a/apps/cli/templates/base/tsconfig.json.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@{{projectName}}/config/tsconfig.base.json" -} diff --git a/apps/cli/templates/packages/config/package.json.hbs b/apps/cli/templates/packages/config/package.json.hbs index 9763e199..a2dfaa28 100644 --- a/apps/cli/templates/packages/config/package.json.hbs +++ b/apps/cli/templates/packages/config/package.json.hbs @@ -1,6 +1,5 @@ { "name": "@{{projectName}}/config", "version": "0.0.0", - "private": true, - "description": "Shared configuration files for {{projectName}} monorepo" + "private": true } diff --git a/apps/cli/templates/packages/config/tsconfig.json b/apps/cli/templates/packages/config/tsconfig.json deleted file mode 100644 index a7aa0efb..00000000 --- a/apps/cli/templates/packages/config/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "noEmit": true - }, - "include": ["."] -}