mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
generate better readme
This commit is contained in:
5
.changeset/eager-lizards-lose.md
Normal file
5
.changeset/eager-lizards-lose.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-better-t-stack": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
generate better readme
|
||||||
@@ -27,12 +27,12 @@ Follow the prompts to configure your project or use the `--yes` flag for default
|
|||||||
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **TypeScript** | End-to-end type safety across all parts of your application |
|
| **TypeScript** | End-to-end type safety across all parts of your application |
|
||||||
| **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• SolidJS<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
|
| **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• SolidJS<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
|
||||||
| **Backend** | • Hono<br>• Express<br>• Elysia<br>• Next.js API routes<br>• Convex<br>• Fastify<br>• None |
|
| **Backend** | • Hono<br>• Express<br>• Elysia<br>• Next.js API routes<br>• Convex<br>• Fastify<br>• None |
|
||||||
| **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• None |
|
| **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• None |
|
||||||
| **Runtime** | • Bun<br>• Node.js |
|
| **Runtime** | • Bun<br>• Node.js<br>• Cloudflare Workers<br>• None |
|
||||||
| **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
|
| **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
|
||||||
| **ORM** | • Drizzle (TypeScript-first)<br>• Prisma (feature-rich)<br>• Mongoose (for MongoDB)<br>• None |
|
| **ORM** | • Drizzle (TypeScript-first)<br>• Prisma (feature-rich)<br>• Mongoose (for MongoDB)<br>• None |
|
||||||
| **Database Setup** | • Turso (SQLite)<br>• Neon (PostgreSQL)<br>• Prisma Postgres (via Prisma Accelerate)<br>• MongoDB Atlas<br>• None (manual setup) |
|
| **Database Setup** | • Turso (SQLite)<br>• Cloudflare D1 (SQLite)<br>• Neon (PostgreSQL)<br>• Supabase (PostgreSQL)<br>• Prisma Postgres (via Prisma Accelerate)<br>• MongoDB Atlas<br>• None (manual setup) |
|
||||||
| **Authentication** | Better-Auth (email/password, with more options coming soon) |
|
| **Authentication** | Better-Auth (email/password, with more options coming soon) |
|
||||||
| **Styling** | Tailwind CSS with shadcn/ui components |
|
| **Styling** | Tailwind CSS with shadcn/ui components |
|
||||||
| **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Husky (Git hooks)<br>• Turborepo (optimized builds) |
|
| **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Husky (Git hooks)<br>• Turborepo (optimized builds) |
|
||||||
@@ -59,9 +59,9 @@ Options:
|
|||||||
--package-manager <pm> Package manager (npm, pnpm, bun)
|
--package-manager <pm> Package manager (npm, pnpm, bun)
|
||||||
--install Install dependencies
|
--install Install dependencies
|
||||||
--no-install Skip installing dependencies
|
--no-install Skip installing dependencies
|
||||||
--db-setup <setup> Database setup (turso, neon, prisma-postgres, mongodb-atlas, none)
|
--db-setup <setup> Database setup (turso, d1, neon, supabase, prisma-postgres, mongodb-atlas, none)
|
||||||
--backend <framework> Backend framework (hono, express, elysia, next, convex, fastify, none)
|
--backend <framework> Backend framework (hono, express, elysia, next, convex, fastify, none)
|
||||||
--runtime <runtime> Runtime (bun, node, none)
|
--runtime <runtime> Runtime (bun, node, workers, none)
|
||||||
--api <type> API type (trpc, orpc, none)
|
--api <type> API type (trpc, orpc, none)
|
||||||
-h, --help Display help
|
-h, --help Display help
|
||||||
```
|
```
|
||||||
@@ -104,6 +104,12 @@ Create a project with Turso database setup:
|
|||||||
npx create-better-t-stack my-app --database sqlite --orm drizzle --db-setup turso
|
npx create-better-t-stack my-app --database sqlite --orm drizzle --db-setup turso
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Create a project with Supabase PostgreSQL setup:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-better-t-stack my-app --database postgres --orm drizzle --db-setup supabase --auth
|
||||||
|
```
|
||||||
|
|
||||||
Create a project with Convex backend:
|
Create a project with Convex backend:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -116,10 +122,48 @@ Create a project with documentation site:
|
|||||||
npx create-better-t-stack my-app --addons starlight
|
npx create-better-t-stack my-app --addons starlight
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Create a minimal TypeScript project with no backend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-better-t-stack my-app --backend none --frontend tanstack-router
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a backend-only project with no frontend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-better-t-stack my-app --frontend none --backend hono --database postgres --orm drizzle
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a simple frontend-only project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-better-t-stack my-app --backend none --frontend next --addons none --examples none
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a Cloudflare Workers project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-better-t-stack my-app --backend hono --runtime workers --database sqlite --orm drizzle --db-setup d1
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a minimal API-only project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-better-t-stack my-app --frontend none --backend hono --api trpc --database none --addons none
|
||||||
|
```
|
||||||
|
|
||||||
## Compatibility Notes
|
## Compatibility Notes
|
||||||
|
|
||||||
- **Convex backend**: Automatically disables authentication, database, ORM, and API options
|
- **Convex backend**: Automatically disables authentication, database, ORM, and API options
|
||||||
- **Backend 'none'**: If selected, this option will force related options like API, ORM, database, authentication, and runtime to 'none'. Examples will also be disabled (set to none/empty).
|
- **Backend 'none'**: If selected, this option will force related options like API, ORM, database, authentication, and runtime to 'none'. Examples will also be disabled (set to none/empty).
|
||||||
|
- **Frontend 'none'**: Creates a backend-only project. When selected, PWA, Tauri, and certain examples may be disabled.
|
||||||
|
- **API 'none'**: Disables tRPC/oRPC setup. Can be used with backend frameworks for REST APIs or custom API implementations.
|
||||||
|
- **Database 'none'**: Disables database setup. Automatically sets ORM to 'none' and disables authentication.
|
||||||
|
- **ORM 'none'**: Can be used when you want to handle database operations manually or use a different ORM.
|
||||||
|
- **Runtime 'none'**: Only available with Convex backend or when backend is 'none'.
|
||||||
|
- **Cloudflare Workers runtime**: Only compatible with Hono backend, Drizzle ORM (or no ORM), and SQLite database (with D1 setup). Not compatible with MongoDB.
|
||||||
|
- **Addons 'none'**: Skips all addons (PWA, Tauri, Starlight, Biome, Husky, Turborepo).
|
||||||
|
- **Examples 'none'**: Skips all example implementations (todo, AI chat).
|
||||||
- **SvelteKit, Nuxt, and SolidJS** frontends are only compatible with oRPC API layer
|
- **SvelteKit, Nuxt, and SolidJS** frontends are only compatible with oRPC API layer
|
||||||
- **PWA support** requires React with TanStack Router, React Router, or SolidJS
|
- **PWA support** requires React with TanStack Router, React Router, or SolidJS
|
||||||
- **Tauri desktop app** requires React (TanStack Router/React Router), Nuxt, SvelteKit, or SolidJS
|
- **Tauri desktop app** requires React (TanStack Router/React Router), Nuxt, SvelteKit, or SolidJS
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export const dependencyVersionMap = {
|
|||||||
"@trpc/server": "^11.4.2",
|
"@trpc/server": "^11.4.2",
|
||||||
"@trpc/client": "^11.4.2",
|
"@trpc/client": "^11.4.2",
|
||||||
|
|
||||||
convex: "^1.23.0",
|
convex: "^1.25.0",
|
||||||
"@convex-dev/react-query": "^0.0.0-alpha.8",
|
"@convex-dev/react-query": "^0.0.0-alpha.8",
|
||||||
"convex-svelte": "^0.0.11",
|
"convex-svelte": "^0.0.11",
|
||||||
|
|
||||||
|
|||||||
@@ -38,15 +38,10 @@ function generateReadmeContent(options: ProjectConfig): string {
|
|||||||
|
|
||||||
const isConvex = backend === "convex";
|
const isConvex = backend === "convex";
|
||||||
const hasReactRouter = frontend.includes("react-router");
|
const hasReactRouter = frontend.includes("react-router");
|
||||||
const hasTanstackRouter = frontend.includes("tanstack-router");
|
|
||||||
const hasNative =
|
const hasNative =
|
||||||
frontend.includes("native-nativewind") ||
|
frontend.includes("native-nativewind") ||
|
||||||
frontend.includes("native-unistyles");
|
frontend.includes("native-unistyles");
|
||||||
const hasNext = frontend.includes("next");
|
|
||||||
const hasTanstackStart = frontend.includes("tanstack-start");
|
|
||||||
const hasSvelte = frontend.includes("svelte");
|
const hasSvelte = frontend.includes("svelte");
|
||||||
const hasSolid = frontend.includes("solid");
|
|
||||||
const hasNuxt = frontend.includes("nuxt");
|
|
||||||
|
|
||||||
const packageManagerRunCmd =
|
const packageManagerRunCmd =
|
||||||
packageManager === "npm" ? "npm run" : packageManager;
|
packageManager === "npm" ? "npm run" : packageManager;
|
||||||
@@ -56,27 +51,18 @@ function generateReadmeContent(options: ProjectConfig): string {
|
|||||||
webPort = "5173";
|
webPort = "5173";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stackDescription = generateStackDescription(
|
||||||
|
frontend,
|
||||||
|
backend,
|
||||||
|
api,
|
||||||
|
isConvex,
|
||||||
|
);
|
||||||
|
|
||||||
return `# ${projectName}
|
return `# ${projectName}
|
||||||
|
|
||||||
This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines ${
|
This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack${
|
||||||
hasTanstackRouter
|
stackDescription ? ` that combines ${stackDescription}` : ""
|
||||||
? "React, TanStack Router"
|
}.
|
||||||
: hasReactRouter
|
|
||||||
? "React, React Router"
|
|
||||||
: hasNext
|
|
||||||
? "Next.js"
|
|
||||||
: hasTanstackStart
|
|
||||||
? "React, TanStack Start"
|
|
||||||
: hasSvelte
|
|
||||||
? "SvelteKit"
|
|
||||||
: hasNuxt
|
|
||||||
? "Nuxt"
|
|
||||||
: hasSolid
|
|
||||||
? "SolidJS"
|
|
||||||
: ""
|
|
||||||
}, ${backend[0].toUpperCase() + backend.slice(1)}${
|
|
||||||
isConvex ? "" : `, ${api.toUpperCase()}`
|
|
||||||
}, and more.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -119,23 +105,7 @@ Then, run the development server:
|
|||||||
${packageManagerRunCmd} dev
|
${packageManagerRunCmd} dev
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
${
|
${generateRunningInstructions(frontend, backend, webPort, hasNative, isConvex)}
|
||||||
hasTanstackRouter ||
|
|
||||||
hasReactRouter ||
|
|
||||||
hasNext ||
|
|
||||||
hasTanstackStart ||
|
|
||||||
hasSvelte ||
|
|
||||||
hasNuxt ||
|
|
||||||
hasSolid
|
|
||||||
? `Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
${hasNative ? "Use the Expo Go app to run the mobile application.\n" : ""}
|
|
||||||
${
|
|
||||||
isConvex
|
|
||||||
? "Your app will connect to the Convex cloud backend automatically."
|
|
||||||
: "The API is running at [http://localhost:3000](http://localhost:3000)."
|
|
||||||
}
|
|
||||||
|
|
||||||
${
|
${
|
||||||
addons.includes("pwa") && hasReactRouter
|
addons.includes("pwa") && hasReactRouter
|
||||||
@@ -146,49 +116,14 @@ ${
|
|||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
\`\`\`
|
\`\`\`
|
||||||
${projectName}/
|
${generateProjectStructure(
|
||||||
├── apps/
|
projectName,
|
||||||
${
|
frontend,
|
||||||
hasTanstackRouter ||
|
backend,
|
||||||
hasReactRouter ||
|
addons,
|
||||||
hasNext ||
|
isConvex,
|
||||||
hasTanstackStart ||
|
api,
|
||||||
hasSvelte ||
|
)}
|
||||||
hasNuxt ||
|
|
||||||
hasSolid
|
|
||||||
? `│ ├── web/ # Frontend application (${
|
|
||||||
hasTanstackRouter
|
|
||||||
? "React + TanStack Router"
|
|
||||||
: hasReactRouter
|
|
||||||
? "React + React Router"
|
|
||||||
: hasNext
|
|
||||||
? "Next.js"
|
|
||||||
: hasTanstackStart
|
|
||||||
? "React + TanStack Start"
|
|
||||||
: hasSvelte
|
|
||||||
? "SvelteKit"
|
|
||||||
: hasNuxt
|
|
||||||
? "Nuxt"
|
|
||||||
: hasSolid
|
|
||||||
? "SolidJS"
|
|
||||||
: ""
|
|
||||||
})\n`
|
|
||||||
: ""
|
|
||||||
}${
|
|
||||||
hasNative
|
|
||||||
? "│ ├── native/ # Mobile application (React Native, Expo)\n"
|
|
||||||
: ""
|
|
||||||
}${
|
|
||||||
addons.includes("starlight")
|
|
||||||
? "│ ├── docs/ # Documentation site (Astro Starlight)\n"
|
|
||||||
: ""
|
|
||||||
}${
|
|
||||||
isConvex
|
|
||||||
? "├── packages/\n│ └── backend/ # Convex backend functions and schema\n"
|
|
||||||
: `│ └── server/ # Backend API (${
|
|
||||||
backend[0].toUpperCase() + backend.slice(1)
|
|
||||||
}, ${api.toUpperCase()})`
|
|
||||||
}
|
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
## Available Scripts
|
## Available Scripts
|
||||||
@@ -205,6 +140,181 @@ ${generateScriptsList(
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateStackDescription(
|
||||||
|
frontend: Frontend[],
|
||||||
|
backend: string,
|
||||||
|
api: API,
|
||||||
|
isConvex: boolean,
|
||||||
|
): string {
|
||||||
|
const parts: string[] = [];
|
||||||
|
|
||||||
|
const hasTanstackRouter = frontend.includes("tanstack-router");
|
||||||
|
const hasReactRouter = frontend.includes("react-router");
|
||||||
|
const hasNext = frontend.includes("next");
|
||||||
|
const hasTanstackStart = frontend.includes("tanstack-start");
|
||||||
|
const hasSvelte = frontend.includes("svelte");
|
||||||
|
const hasNuxt = frontend.includes("nuxt");
|
||||||
|
const hasSolid = frontend.includes("solid");
|
||||||
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
||||||
|
|
||||||
|
if (!hasFrontendNone) {
|
||||||
|
if (hasTanstackRouter) {
|
||||||
|
parts.push("React, TanStack Router");
|
||||||
|
} else if (hasReactRouter) {
|
||||||
|
parts.push("React, React Router");
|
||||||
|
} else if (hasNext) {
|
||||||
|
parts.push("Next.js");
|
||||||
|
} else if (hasTanstackStart) {
|
||||||
|
parts.push("React, TanStack Start");
|
||||||
|
} else if (hasSvelte) {
|
||||||
|
parts.push("SvelteKit");
|
||||||
|
} else if (hasNuxt) {
|
||||||
|
parts.push("Nuxt");
|
||||||
|
} else if (hasSolid) {
|
||||||
|
parts.push("SolidJS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backend !== "none") {
|
||||||
|
parts.push(backend[0].toUpperCase() + backend.slice(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isConvex && api !== "none") {
|
||||||
|
parts.push(api.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.length > 0 ? `${parts.join(", ")}, and more` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRunningInstructions(
|
||||||
|
frontend: Frontend[],
|
||||||
|
backend: string,
|
||||||
|
webPort: string,
|
||||||
|
hasNative: boolean,
|
||||||
|
isConvex: boolean,
|
||||||
|
): string {
|
||||||
|
const instructions: string[] = [];
|
||||||
|
|
||||||
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
||||||
|
const isBackendNone = backend === "none";
|
||||||
|
|
||||||
|
if (!hasFrontendNone) {
|
||||||
|
const hasTanstackRouter = frontend.includes("tanstack-router");
|
||||||
|
const hasReactRouter = frontend.includes("react-router");
|
||||||
|
const hasNext = frontend.includes("next");
|
||||||
|
const hasTanstackStart = frontend.includes("tanstack-start");
|
||||||
|
const hasSvelte = frontend.includes("svelte");
|
||||||
|
const hasNuxt = frontend.includes("nuxt");
|
||||||
|
const hasSolid = frontend.includes("solid");
|
||||||
|
|
||||||
|
if (
|
||||||
|
hasTanstackRouter ||
|
||||||
|
hasReactRouter ||
|
||||||
|
hasNext ||
|
||||||
|
hasTanstackStart ||
|
||||||
|
hasSvelte ||
|
||||||
|
hasNuxt ||
|
||||||
|
hasSolid
|
||||||
|
) {
|
||||||
|
instructions.push(
|
||||||
|
`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNative) {
|
||||||
|
instructions.push("Use the Expo Go app to run the mobile application.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isConvex) {
|
||||||
|
instructions.push(
|
||||||
|
"Your app will connect to the Convex cloud backend automatically.",
|
||||||
|
);
|
||||||
|
} else if (!isBackendNone) {
|
||||||
|
instructions.push(
|
||||||
|
"The API is running at [http://localhost:3000](http://localhost:3000).",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateProjectStructure(
|
||||||
|
projectName: string,
|
||||||
|
frontend: Frontend[],
|
||||||
|
backend: string,
|
||||||
|
addons: Addons[],
|
||||||
|
isConvex: boolean,
|
||||||
|
api: API,
|
||||||
|
): string {
|
||||||
|
const structure: string[] = [`${projectName}/`, "├── apps/"];
|
||||||
|
|
||||||
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
||||||
|
const isBackendNone = backend === "none";
|
||||||
|
|
||||||
|
if (!hasFrontendNone) {
|
||||||
|
const hasTanstackRouter = frontend.includes("tanstack-router");
|
||||||
|
const hasReactRouter = frontend.includes("react-router");
|
||||||
|
const hasNext = frontend.includes("next");
|
||||||
|
const hasTanstackStart = frontend.includes("tanstack-start");
|
||||||
|
const hasSvelte = frontend.includes("svelte");
|
||||||
|
const hasNuxt = frontend.includes("nuxt");
|
||||||
|
const hasSolid = frontend.includes("solid");
|
||||||
|
|
||||||
|
if (
|
||||||
|
hasTanstackRouter ||
|
||||||
|
hasReactRouter ||
|
||||||
|
hasNext ||
|
||||||
|
hasTanstackStart ||
|
||||||
|
hasSvelte ||
|
||||||
|
hasNuxt ||
|
||||||
|
hasSolid
|
||||||
|
) {
|
||||||
|
let frontendType = "";
|
||||||
|
if (hasTanstackRouter) frontendType = "React + TanStack Router";
|
||||||
|
else if (hasReactRouter) frontendType = "React + React Router";
|
||||||
|
else if (hasNext) frontendType = "Next.js";
|
||||||
|
else if (hasTanstackStart) frontendType = "React + TanStack Start";
|
||||||
|
else if (hasSvelte) frontendType = "SvelteKit";
|
||||||
|
else if (hasNuxt) frontendType = "Nuxt";
|
||||||
|
else if (hasSolid) frontendType = "SolidJS";
|
||||||
|
|
||||||
|
structure.push(
|
||||||
|
`│ ├── web/ # Frontend application (${frontendType})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasNative =
|
||||||
|
frontend.includes("native-nativewind") ||
|
||||||
|
frontend.includes("native-unistyles");
|
||||||
|
if (hasNative) {
|
||||||
|
structure.push(
|
||||||
|
"│ ├── native/ # Mobile application (React Native, Expo)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addons.includes("starlight")) {
|
||||||
|
structure.push(
|
||||||
|
"│ ├── docs/ # Documentation site (Astro Starlight)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isConvex) {
|
||||||
|
structure.push("├── packages/");
|
||||||
|
structure.push(
|
||||||
|
"│ └── backend/ # Convex backend functions and schema",
|
||||||
|
);
|
||||||
|
} else if (!isBackendNone) {
|
||||||
|
const backendName = backend[0].toUpperCase() + backend.slice(1);
|
||||||
|
const apiName = api !== "none" ? api.toUpperCase() : "";
|
||||||
|
const backendDesc = apiName ? `${backendName}, ${apiName}` : backendName;
|
||||||
|
structure.push(`│ └── server/ # Backend API (${backendDesc})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return structure.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
function generateFeaturesList(
|
function generateFeaturesList(
|
||||||
database: Database,
|
database: Database,
|
||||||
auth: boolean,
|
auth: boolean,
|
||||||
@@ -216,6 +326,7 @@ function generateFeaturesList(
|
|||||||
api: API,
|
api: API,
|
||||||
): string {
|
): string {
|
||||||
const isConvex = backend === "convex";
|
const isConvex = backend === "convex";
|
||||||
|
const isBackendNone = backend === "none";
|
||||||
const hasTanstackRouter = frontend.includes("tanstack-router");
|
const hasTanstackRouter = frontend.includes("tanstack-router");
|
||||||
const hasReactRouter = frontend.includes("react-router");
|
const hasReactRouter = frontend.includes("react-router");
|
||||||
const hasNative =
|
const hasNative =
|
||||||
@@ -226,29 +337,34 @@ function generateFeaturesList(
|
|||||||
const hasSvelte = frontend.includes("svelte");
|
const hasSvelte = frontend.includes("svelte");
|
||||||
const hasNuxt = frontend.includes("nuxt");
|
const hasNuxt = frontend.includes("nuxt");
|
||||||
const hasSolid = frontend.includes("solid");
|
const hasSolid = frontend.includes("solid");
|
||||||
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
||||||
|
|
||||||
const addonsList = [
|
const addonsList = [
|
||||||
"- **TypeScript** - For type safety and improved developer experience",
|
"- **TypeScript** - For type safety and improved developer experience",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (hasTanstackRouter) {
|
if (!hasFrontendNone) {
|
||||||
addonsList.push(
|
if (hasTanstackRouter) {
|
||||||
"- **TanStack Router** - File-based routing with full type safety",
|
addonsList.push(
|
||||||
);
|
"- **TanStack Router** - File-based routing with full type safety",
|
||||||
} else if (hasReactRouter) {
|
);
|
||||||
addonsList.push("- **React Router** - Declarative routing for React");
|
} else if (hasReactRouter) {
|
||||||
} else if (hasNext) {
|
addonsList.push("- **React Router** - Declarative routing for React");
|
||||||
addonsList.push("- **Next.js** - Full-stack React framework");
|
} else if (hasNext) {
|
||||||
} else if (hasTanstackStart) {
|
addonsList.push("- **Next.js** - Full-stack React framework");
|
||||||
addonsList.push(
|
} else if (hasTanstackStart) {
|
||||||
"- **TanStack Start** - SSR framework with TanStack Router",
|
addonsList.push(
|
||||||
);
|
"- **TanStack Start** - SSR framework with TanStack Router",
|
||||||
} else if (hasSvelte) {
|
);
|
||||||
addonsList.push("- **SvelteKit** - Web framework for building Svelte apps");
|
} else if (hasSvelte) {
|
||||||
} else if (hasNuxt) {
|
addonsList.push(
|
||||||
addonsList.push("- **Nuxt** - The Intuitive Vue Framework");
|
"- **SvelteKit** - Web framework for building Svelte apps",
|
||||||
} else if (hasSolid) {
|
);
|
||||||
addonsList.push("- **SolidJS** - Simple and performant reactivity");
|
} else if (hasNuxt) {
|
||||||
|
addonsList.push("- **Nuxt** - The Intuitive Vue Framework");
|
||||||
|
} else if (hasSolid) {
|
||||||
|
addonsList.push("- **SolidJS** - Simple and performant reactivity");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasNative) {
|
if (hasNative) {
|
||||||
@@ -256,14 +372,16 @@ function generateFeaturesList(
|
|||||||
addonsList.push("- **Expo** - Tools for React Native development");
|
addonsList.push("- **Expo** - Tools for React Native development");
|
||||||
}
|
}
|
||||||
|
|
||||||
addonsList.push(
|
if (!hasFrontendNone) {
|
||||||
"- **TailwindCSS** - Utility-first CSS for rapid UI development",
|
addonsList.push(
|
||||||
"- **shadcn/ui** - Reusable UI components",
|
"- **TailwindCSS** - Utility-first CSS for rapid UI development",
|
||||||
);
|
"- **shadcn/ui** - Reusable UI components",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isConvex) {
|
if (isConvex) {
|
||||||
addonsList.push("- **Convex** - Reactive backend-as-a-service platform");
|
addonsList.push("- **Convex** - Reactive backend-as-a-service platform");
|
||||||
} else {
|
} else if (!isBackendNone) {
|
||||||
if (backend === "hono") {
|
if (backend === "hono") {
|
||||||
addonsList.push("- **Hono** - Lightweight, performant server framework");
|
addonsList.push("- **Hono** - Lightweight, performant server framework");
|
||||||
} else if (backend === "express") {
|
} else if (backend === "express") {
|
||||||
@@ -284,25 +402,38 @@ function generateFeaturesList(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
addonsList.push(
|
if (runtime !== "none") {
|
||||||
`- **${runtime === "bun" ? "Bun" : "Node.js"}** - Runtime environment`,
|
addonsList.push(
|
||||||
);
|
`- **${
|
||||||
|
runtime === "bun" ? "Bun" : runtime === "node" ? "Node.js" : runtime
|
||||||
|
}** - Runtime environment`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (database !== "none" && !isConvex) {
|
if (database !== "none" && !isConvex) {
|
||||||
|
const ormName =
|
||||||
|
orm === "drizzle"
|
||||||
|
? "Drizzle"
|
||||||
|
: orm === "prisma"
|
||||||
|
? "Prisma"
|
||||||
|
: orm === "mongoose"
|
||||||
|
? "Mongoose"
|
||||||
|
: "ORM";
|
||||||
|
const dbName =
|
||||||
|
database === "sqlite"
|
||||||
|
? "SQLite/Turso"
|
||||||
|
: database === "postgres"
|
||||||
|
? "PostgreSQL"
|
||||||
|
: database === "mysql"
|
||||||
|
? "MySQL"
|
||||||
|
: database === "mongodb"
|
||||||
|
? "MongoDB"
|
||||||
|
: "Database";
|
||||||
|
|
||||||
addonsList.push(
|
addonsList.push(
|
||||||
`- **${
|
`- **${ormName}** - TypeScript-first ORM`,
|
||||||
orm === "drizzle" ? "Drizzle" : orm === "prisma" ? "Prisma" : "Mongoose"
|
`- **${dbName}** - Database engine`,
|
||||||
}** - TypeScript-first ORM`,
|
|
||||||
`- **${
|
|
||||||
database === "sqlite"
|
|
||||||
? "SQLite/Turso"
|
|
||||||
: database === "postgres"
|
|
||||||
? "PostgreSQL"
|
|
||||||
: database === "mysql"
|
|
||||||
? "MySQL"
|
|
||||||
: "MongoDB"
|
|
||||||
}** - Database engine`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +464,7 @@ function generateFeaturesList(
|
|||||||
|
|
||||||
function generateDatabaseSetup(
|
function generateDatabaseSetup(
|
||||||
database: Database,
|
database: Database,
|
||||||
auth: boolean,
|
_auth: boolean,
|
||||||
packageManagerRunCmd: string,
|
packageManagerRunCmd: string,
|
||||||
orm: ORM,
|
orm: ORM,
|
||||||
): string {
|
): string {
|
||||||
@@ -345,7 +476,11 @@ function generateDatabaseSetup(
|
|||||||
|
|
||||||
if (database === "sqlite") {
|
if (database === "sqlite") {
|
||||||
setup += `This project uses SQLite${
|
setup += `This project uses SQLite${
|
||||||
orm === "drizzle" ? " with Drizzle ORM" : " with Prisma"
|
orm === "drizzle"
|
||||||
|
? " with Drizzle ORM"
|
||||||
|
: orm === "prisma"
|
||||||
|
? " with Prisma"
|
||||||
|
: ` with ${orm}`
|
||||||
}.
|
}.
|
||||||
|
|
||||||
1. Start the local SQLite database:
|
1. Start the local SQLite database:
|
||||||
@@ -357,7 +492,11 @@ cd apps/server && ${packageManagerRunCmd} db:local
|
|||||||
`;
|
`;
|
||||||
} else if (database === "postgres") {
|
} else if (database === "postgres") {
|
||||||
setup += `This project uses PostgreSQL${
|
setup += `This project uses PostgreSQL${
|
||||||
orm === "drizzle" ? " with Drizzle ORM" : " with Prisma"
|
orm === "drizzle"
|
||||||
|
? " with Drizzle ORM"
|
||||||
|
: orm === "prisma"
|
||||||
|
? " with Prisma"
|
||||||
|
: ` with ${orm}`
|
||||||
}.
|
}.
|
||||||
|
|
||||||
1. Make sure you have a PostgreSQL database set up.
|
1. Make sure you have a PostgreSQL database set up.
|
||||||
@@ -365,7 +504,11 @@ cd apps/server && ${packageManagerRunCmd} db:local
|
|||||||
`;
|
`;
|
||||||
} else if (database === "mysql") {
|
} else if (database === "mysql") {
|
||||||
setup += `This project uses MySQL${
|
setup += `This project uses MySQL${
|
||||||
orm === "drizzle" ? " with Drizzle ORM" : " with Prisma"
|
orm === "drizzle"
|
||||||
|
? " with Drizzle ORM"
|
||||||
|
: orm === "prisma"
|
||||||
|
? " with Prisma"
|
||||||
|
: ` with ${orm}`
|
||||||
}.
|
}.
|
||||||
|
|
||||||
1. Make sure you have a MySQL database set up.
|
1. Make sure you have a MySQL database set up.
|
||||||
@@ -373,7 +516,11 @@ cd apps/server && ${packageManagerRunCmd} db:local
|
|||||||
`;
|
`;
|
||||||
} else if (database === "mongodb") {
|
} else if (database === "mongodb") {
|
||||||
setup += `This project uses MongoDB ${
|
setup += `This project uses MongoDB ${
|
||||||
orm === "mongoose" ? "with Mongoose" : "with Prisma ORM"
|
orm === "mongoose"
|
||||||
|
? "with Mongoose"
|
||||||
|
: orm === "prisma"
|
||||||
|
? "with Prisma ORM"
|
||||||
|
: `with ${orm}`
|
||||||
}.
|
}.
|
||||||
|
|
||||||
1. Make sure you have MongoDB set up.
|
1. Make sure you have MongoDB set up.
|
||||||
@@ -382,7 +529,7 @@ cd apps/server && ${packageManagerRunCmd} db:local
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup += `
|
setup += `
|
||||||
${auth ? "3" : "3"}. ${
|
3. ${
|
||||||
orm === "prisma"
|
orm === "prisma"
|
||||||
? `Generate the Prisma client and push the schema:
|
? `Generate the Prisma client and push the schema:
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
@@ -413,15 +560,18 @@ function generateScriptsList(
|
|||||||
backend: string,
|
backend: string,
|
||||||
): string {
|
): string {
|
||||||
const isConvex = backend === "convex";
|
const isConvex = backend === "convex";
|
||||||
|
const isBackendNone = backend === "none";
|
||||||
|
|
||||||
let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
|
let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
|
||||||
- \`${packageManagerRunCmd} build\`: Build all applications
|
- \`${packageManagerRunCmd} build\`: Build all applications`;
|
||||||
|
|
||||||
|
scripts += `
|
||||||
- \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
|
- \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
|
||||||
|
|
||||||
if (isConvex) {
|
if (isConvex) {
|
||||||
scripts += `
|
scripts += `
|
||||||
- \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
|
- \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
|
||||||
} else {
|
} else if (!isBackendNone) {
|
||||||
scripts += `
|
scripts += `
|
||||||
- \`${packageManagerRunCmd} dev:server\`: Start only the server`;
|
- \`${packageManagerRunCmd} dev:server\`: Start only the server`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ export function displayPostInstallInstructions(
|
|||||||
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup ${pc.dim(
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup ${pc.dim(
|
||||||
"(this will guide you through Convex project setup)",
|
"(this will guide you through Convex project setup)",
|
||||||
)}\n`;
|
)}\n`;
|
||||||
|
output += `${pc.cyan(
|
||||||
|
`${stepCounter++}.`,
|
||||||
|
)} Copy environment variables from ${pc.white(
|
||||||
|
"packages/backend/.env.local",
|
||||||
|
)} \nto ${pc.white("apps/*/.env")}\n`;
|
||||||
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
|
||||||
} else {
|
} else {
|
||||||
if (runtime !== "workers") {
|
if (runtime !== "workers") {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ async function updateRootPackageJson(
|
|||||||
scripts["dev:web"] = "turbo -F web dev";
|
scripts["dev:web"] = "turbo -F web dev";
|
||||||
scripts["dev:server"] = serverDevScript;
|
scripts["dev:server"] = serverDevScript;
|
||||||
if (options.backend === "convex") {
|
if (options.backend === "convex") {
|
||||||
scripts["dev:setup"] = `turbo -F ${backendPackageName} setup`;
|
scripts["dev:setup"] = `turbo -F ${backendPackageName} dev:setup`;
|
||||||
}
|
}
|
||||||
if (needsDbScripts) {
|
if (needsDbScripts) {
|
||||||
scripts["db:push"] = `turbo -F ${backendPackageName} db:push`;
|
scripts["db:push"] = `turbo -F ${backendPackageName} db:push`;
|
||||||
@@ -89,7 +89,7 @@ async function updateRootPackageJson(
|
|||||||
scripts["dev:web"] = "pnpm --filter web dev";
|
scripts["dev:web"] = "pnpm --filter web dev";
|
||||||
scripts["dev:server"] = serverDevScript;
|
scripts["dev:server"] = serverDevScript;
|
||||||
if (options.backend === "convex") {
|
if (options.backend === "convex") {
|
||||||
scripts["dev:setup"] = `pnpm --filter ${backendPackageName} setup`;
|
scripts["dev:setup"] = `pnpm --filter ${backendPackageName} dev:setup`;
|
||||||
}
|
}
|
||||||
if (needsDbScripts) {
|
if (needsDbScripts) {
|
||||||
scripts["db:push"] = `pnpm --filter ${backendPackageName} db:push`;
|
scripts["db:push"] = `pnpm --filter ${backendPackageName} db:push`;
|
||||||
@@ -114,7 +114,8 @@ async function updateRootPackageJson(
|
|||||||
scripts["dev:web"] = "npm run dev --workspace web";
|
scripts["dev:web"] = "npm run dev --workspace web";
|
||||||
scripts["dev:server"] = serverDevScript;
|
scripts["dev:server"] = serverDevScript;
|
||||||
if (options.backend === "convex") {
|
if (options.backend === "convex") {
|
||||||
scripts["dev:setup"] = `npm run setup --workspace ${backendPackageName}`;
|
scripts["dev:setup"] =
|
||||||
|
`npm run dev:setup --workspace ${backendPackageName}`;
|
||||||
}
|
}
|
||||||
if (needsDbScripts) {
|
if (needsDbScripts) {
|
||||||
scripts["db:push"] = `npm run db:push --workspace ${backendPackageName}`;
|
scripts["db:push"] = `npm run db:push --workspace ${backendPackageName}`;
|
||||||
@@ -140,7 +141,7 @@ async function updateRootPackageJson(
|
|||||||
scripts["dev:web"] = "bun run --filter web dev";
|
scripts["dev:web"] = "bun run --filter web dev";
|
||||||
scripts["dev:server"] = serverDevScript;
|
scripts["dev:server"] = serverDevScript;
|
||||||
if (options.backend === "convex") {
|
if (options.backend === "convex") {
|
||||||
scripts["dev:setup"] = `bun run --filter ${backendPackageName} setup`;
|
scripts["dev:setup"] = `bun run --filter ${backendPackageName} dev:setup`;
|
||||||
}
|
}
|
||||||
if (needsDbScripts) {
|
if (needsDbScripts) {
|
||||||
scripts["db:push"] = `bun run --filter ${backendPackageName} db:push`;
|
scripts["db:push"] = `bun run --filter ${backendPackageName} db:push`;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"cache": false,
|
"cache": false,
|
||||||
"persistent": true
|
"persistent": true
|
||||||
}{{#if (eq backend "convex")}},
|
}{{#if (eq backend "convex")}},
|
||||||
"setup": {
|
"dev:setup": {
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"persistent": true
|
"persistent": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "convex dev",
|
"dev": "convex dev",
|
||||||
"setup": "convex dev --configure --until-success"
|
"dev:setup": "convex dev --configure --until-success"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
@@ -12,6 +12,6 @@
|
|||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"convex": "^1.23.0"
|
"convex": "^1.25.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ const showcaseProjects = [
|
|||||||
{
|
{
|
||||||
title: "Screenshothis",
|
title: "Screenshothis",
|
||||||
description: "Your All-in-One Screenshot Solution",
|
description: "Your All-in-One Screenshot Solution",
|
||||||
imageUrl: "https://api.screenshothis.com/v1/screenshots/take?api_key=ss_live_NQJgRXqHcKPwnoMTuQmgiwLIGbVfihjpMyQhgsaMyNBHTyesvrxpYNXmdgcnxipc&url=https%3A%2F%2Fscreenshothis.com%2F&width=1200&height=630&device_scale_factor=0.75&block_ads=true&block_cookie_banners=true&block_trackers=true&prefers_color_scheme=light&prefers_reduced_motion=reduce&is_cached=true&cache_key=cfb06bf3616b1d03bdf455628a3830120e2080dd",
|
imageUrl:
|
||||||
liveUrl: "https://screenshothis.com?utm_source=better-t-stack&utm_medium=showcase&utm_campaign=referer",
|
"https://api.screenshothis.com/v1/screenshots/take?api_key=ss_live_NQJgRXqHcKPwnoMTuQmgiwLIGbVfihjpMyQhgsaMyNBHTyesvrxpYNXmdgcnxipc&url=https%3A%2F%2Fscreenshothis.com%2F&width=1200&height=630&device_scale_factor=0.75&block_ads=true&block_cookie_banners=true&block_trackers=true&prefers_color_scheme=light&prefers_reduced_motion=reduce&is_cached=true&cache_key=cfb06bf3616b1d03bdf455628a3830120e2080dd",
|
||||||
|
liveUrl:
|
||||||
|
"https://screenshothis.com?utm_source=better-t-stack&utm_medium=showcase&utm_campaign=referer",
|
||||||
tags: [
|
tags: [
|
||||||
"oRPC",
|
"oRPC",
|
||||||
"TanStack Start (devinxi)",
|
"TanStack Start (devinxi)",
|
||||||
|
|||||||
Reference in New Issue
Block a user