diff --git a/cli/bun.lockb b/cli/bun.lockb index b48249e..68b1839 100755 Binary files a/cli/bun.lockb and b/cli/bun.lockb differ diff --git a/cli/package.json b/cli/package.json index 9900eb9..6841056 100644 --- a/cli/package.json +++ b/cli/package.json @@ -25,6 +25,7 @@ "dependencies": { "@clack/prompts": "^0.8.1", "chalk": "^5.3.0", + "picocolors": "^0.2.1", "execa": "^9.5.2", "fs-extra": "^11.2.0", "hono": "^4.7.0", diff --git a/cli/src/cli/index.ts b/cli/src/cli/index.ts index 0d14bbb..19d5d8d 100644 --- a/cli/src/cli/index.ts +++ b/cli/src/cli/index.ts @@ -4,7 +4,7 @@ import { getUserPkgManager } from "@/utils/get-user-pkg-manager.js" export interface CliResults { projectName: string - orm: "none" | "drizzle" | undefined + orm: "none" | "drizzle" | "prisma" | undefined dialect?: "postgres" | undefined provider?: "neon" | "postgres" | "vercel-postgres" | "planetscale" | undefined noInstall?: boolean @@ -40,11 +40,12 @@ export async function runCli(): Promise { return undefined } - const orm = await select<"none" | "drizzle">({ + const orm = await select<"none" | "drizzle" | "prisma">({ message: "Which database ORM would you like to use?", options: [ { value: "none", label: "None" }, { value: "drizzle", label: "Drizzle ORM" }, + { value: "prisma", label: "Prisma ORM" }, ], }) @@ -57,7 +58,6 @@ export async function runCli(): Promise { let provider = undefined if (orm === "drizzle") { dialect = "postgres" as const // Only offering postgres - provider = await select({ message: "Which Postgres provider would you like to use?", options: [ @@ -66,11 +66,12 @@ export async function runCli(): Promise { { value: "vercel-postgres", label: "Vercel Postgres" }, ], }) - if (isCancel(provider)) { outro("Setup cancelled.") return undefined } + } else if (orm === "prisma") { + // Prisma CLI does not need a separate provider installer } let noInstall = noInstallFlag diff --git a/cli/src/helpers/install-base-template.ts b/cli/src/helpers/install-base-template.ts index 93bf415..bbe1d76 100644 --- a/cli/src/helpers/install-base-template.ts +++ b/cli/src/helpers/install-base-template.ts @@ -3,6 +3,7 @@ import * as p from "@clack/prompts" import chalk from "chalk" import fs from "fs-extra" import ora from "ora" +import { execa } from "execa" import { PKG_ROOT } from "@/constants.js" import { type InstallerOptions } from "@/installers/index.js" @@ -103,4 +104,13 @@ export const installBaseTemplate = async ({ spinner.succeed( `${scaffoldedName} ${chalk.green("scaffolded successfully!")}\n`, ) + // Initialize a git repository and make initial commit + try { + await execa("git", ["init"], { cwd: projectDir }) + await execa("git", ["add", "."], { cwd: projectDir }) + await execa("git", ["commit", "-m", "Initial commit"], { cwd: projectDir }) + logger.info("Initialized git repository") + } catch { + // ignore if git is not available + } } diff --git a/cli/src/installers/dep-version-map.ts b/cli/src/installers/dep-version-map.ts index e565cba..70b430a 100644 --- a/cli/src/installers/dep-version-map.ts +++ b/cli/src/installers/dep-version-map.ts @@ -4,22 +4,27 @@ */ export const dependencyVersionMap = { // neon - "@neondatabase/serverless": "^0.10.4", + "@neondatabase/serverless": "*", // vercel postgres - "@vercel/postgres": "^0.10.0", + "@vercel/postgres": "*", // Drizzle - "drizzle-kit": "^0.30.1", - "drizzle-orm": "^0.39.0", - "eslint-plugin-drizzle": "^0.2.3", - "@planetscale/database": "^1.19.0", - postgres: "^3.4.5", + "drizzle-kit": "*", + "drizzle-orm": "*", + "eslint-plugin-drizzle": "*", + "@planetscale/database": "*", + postgres: "*", // TailwindCSS - tailwindcss: "^4.0.0", - postcss: "^8.5.1", - prettier: "^3.3.2", - "prettier-plugin-tailwindcss": "^0.6.11", + tailwindcss: "*", + postcss: "*", + prettier: "*", + "prettier-plugin-tailwindcss": "*", + + // Prisma + prisma: "*", + "@prisma/client": "*", + "@prisma/extension-accelerate": "*", } as const export type AvailableDependencies = keyof typeof dependencyVersionMap diff --git a/cli/src/installers/index.ts b/cli/src/installers/index.ts index 1d3091a..0d78184 100644 --- a/cli/src/installers/index.ts +++ b/cli/src/installers/index.ts @@ -1,5 +1,6 @@ import { PackageManager } from "@/utils/get-user-pkg-manager.js" import { drizzleInstaller } from "./drizzle.js" +import { prismaInstaller } from "./prisma.js" import { neonInstaller } from "./neon.js" import { noOrmInstaller } from "./no-orm.js" import { postgresInstaller } from "./postgres.js" @@ -8,7 +9,7 @@ import { planetscaleInstaller } from "./planetscale.js" // Turning this into a const allows the list to be iterated over for programmatically creating prompt options // Should increase extensibility in the future -export const orms = ["none", "drizzle"] as const +export const orms = ["none", "drizzle", "prisma"] as const export type Orm = (typeof orms)[number] export const dialects = ["postgres"] as const @@ -57,6 +58,10 @@ export const buildInstallerMap = ( inUse: selectedOrm === "drizzle", installer: drizzleInstaller, }, + prisma: { + inUse: selectedOrm === "prisma", + installer: prismaInstaller, + }, }, provider: { postgres: { diff --git a/cli/src/installers/prisma.ts b/cli/src/installers/prisma.ts new file mode 100644 index 0000000..f0731cb --- /dev/null +++ b/cli/src/installers/prisma.ts @@ -0,0 +1,64 @@ +import fs from "fs-extra" +import path from "path" +import { type PackageJson } from "type-fest" + +import { PKG_ROOT } from "@/constants.js" +import { addPackageDependency } from "@/utils/add-package-dep.js" +import { Installer } from "./index.js" + +export const prismaInstaller: Installer = ({ projectDir }) => { + // Add Prisma CLI as a dev dependency and Prisma Client as a dependency + addPackageDependency({ + projectDir, + dependencies: ["prisma"], + devDependencies: true, + }) + // Add Prisma Accelerate for Edge support + addPackageDependency({ + projectDir, + dependencies: ["@prisma/extension-accelerate"], + devDependencies: false, + }) + addPackageDependency({ + projectDir, + dependencies: ["@prisma/client"], + devDependencies: false, + }) + + const extrasDir = path.join(PKG_ROOT, "template/extras") + + // Copy .env template + const envSrc = path.join(extrasDir, "config/_env-prisma") + const envDest = path.join(projectDir, ".env") + + // Copy Prisma schema + const schemaSrc = path.join(extrasDir, "config/schema.prisma") + const schemaDest = path.join(projectDir, "prisma/schema.prisma") + + // Copy Prisma client helper + const clientSrc = path.join(extrasDir, "src/lib/prisma.ts") + const clientDest = path.join(projectDir, "src/lib/prisma.ts") + + fs.ensureDirSync(path.dirname(envDest)) + fs.ensureDirSync(path.dirname(schemaDest)) + fs.ensureDirSync(path.dirname(clientDest)) + + fs.copySync(envSrc, envDest) + fs.copySync(schemaSrc, schemaDest) + fs.copySync(clientSrc, clientDest) + // Copy JStack middleware for Prisma + const jstackSrc = path.join(extrasDir, "src/server/jstack/prisma.ts") + const jstackDest = path.join(projectDir, "src/server/jstack.ts") + fs.ensureDirSync(path.dirname(jstackDest)) + fs.copySync(jstackSrc, jstackDest) + + // Update package.json scripts + const pkgJsonPath = path.join(projectDir, "package.json") + const pkgJson = fs.readJSONSync(pkgJsonPath) as PackageJson + pkgJson.scripts = { + ...pkgJson.scripts, + "prisma:migrate": "prisma migrate dev", + "prisma:generate": "prisma generate --no-engine", + } + fs.writeJSONSync(pkgJsonPath, pkgJson, { spaces: 2 }) +} diff --git a/cli/template/base-assets/base-package.json b/cli/template/base-assets/base-package.json index 158f8d0..9750a81 100644 --- a/cli/template/base-assets/base-package.json +++ b/cli/template/base-assets/base-package.json @@ -9,29 +9,31 @@ "lint": "next lint" }, "dependencies": { - "@tailwindcss/postcss": "^4.0.0", - "@tanstack/react-query": "^5.51.23", - "clsx": "^2.1.1", - "hono": "^4.7.0", - "jstack": "^1.1.1", - "next": "^15.1.6", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "superjson": "^2.2.1", - "tailwind-merge": "^2.5.2", - "zod": "^3.24.1" + "@tailwindcss/postcss": "*", + "@tanstack/react-query": "*", + "clsx": "*", + "drizzle-orm": "*", + "hono": "*", + "jstack": "*", + "next": "*", + "react": "*", + "react-dom": "*", + "superjson": "*", + "tailwind-merge": "*", + "zod": "*" }, "devDependencies": { - "dotenv": "^16.4.5", - "@cloudflare/workers-types": "^4.20250214.0", - "@types/node": "^22.10.6", - "@types/react": "^19.0.7", - "@types/react-dom": "^19.0.3", - "eslint": "^9.18.0", - "eslint-config-next": "^15.1.4", - "postcss": "^8", - "tailwindcss": "^4.0.0", - "typescript": "^5", - "wrangler": "^4.0.0" + "dotenv": "*", + "@cloudflare/workers-types": "*", + "@types/node": "*", + "@types/react": "*", + "@types/react-dom": "*", + "drizzle-kit": "*", + "eslint": "*", + "eslint-config-next": "*", + "postcss": "*", + "tailwindcss": "*", + "typescript": "*", + "wrangler": "*" } } diff --git a/cli/template/base/wrangler.json b/cli/template/base/wrangler.json new file mode 100644 index 0000000..d931670 --- /dev/null +++ b/cli/template/base/wrangler.json @@ -0,0 +1,30 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "jstack-app", + "main": "src/server/index.ts", + "compatibility_date": "2025-03-22", + "compatibility_flags": [ + "nodejs_compat", + "nodejs_als", + "no_handle_cross_request_promise_resolution" + ], + // Add these in if you want to use a custom domain + // "routes": [ + // { + // "pattern": "sub.domain.com", + // "custom_domain": true + // } + // ], + // "workers_dev": false, + // "preview_urls": false, + "observability": { + "enabled": true, + "head_sampling_rate": 1 + }, + "placement": { + "mode": "smart" + }, + "dev": { + "port": 8080 + } +} diff --git a/package.json b/package.json index d92c531..7ab5e8d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,12 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "keywords": ["jstack", "next.js", "typescript", "hono"], + "keywords": [ + "jstack", + "next.js", + "typescript", + "hono" + ], "dependencies": { "prettier": "^3.4.2" }