Files
create-better-t-stack/apps/cli/src/helpers/create-readme.ts
Aman Varshney 8adf020c2a add svelte
2025-04-26 11:42:15 +05:30

389 lines
10 KiB
TypeScript

import path from "node:path";
import consola from "consola";
import fs from "fs-extra";
import type {
ProjectAddons,
ProjectConfig,
ProjectDatabase,
ProjectFrontend,
ProjectOrm,
ProjectRuntime,
} from "../types";
export async function createReadme(projectDir: string, options: ProjectConfig) {
const readmePath = path.join(projectDir, "README.md");
const content = generateReadmeContent(options);
try {
await fs.writeFile(readmePath, content);
} catch (error) {
consola.error("Failed to create README.md file:", error);
}
}
function generateReadmeContent(options: ProjectConfig): string {
const {
projectName,
packageManager,
database,
auth,
addons = [],
orm = "drizzle",
runtime = "bun",
frontend = ["tanstack-router"],
backend = "hono",
} = options;
const hasReactRouter = frontend.includes("react-router");
const hasTanstackRouter = frontend.includes("tanstack-router");
const hasNative = frontend.includes("native");
const hasNext = frontend.includes("next");
const hasTanstackStart = frontend.includes("tanstack-start");
const hasSvelte = frontend.includes("svelte");
const hasNuxt = frontend.includes("nuxt");
const packageManagerRunCmd =
packageManager === "npm" ? "npm run" : packageManager;
let webPort = "3001";
if (hasReactRouter || hasSvelte) {
webPort = "5173";
}
return `# ${projectName}
This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines React, ${
hasTanstackRouter
? "TanStack Router"
: hasReactRouter
? "React Router"
: hasNext
? "Next.js"
: hasTanstackStart
? "TanStack Start"
: hasSvelte
? "SvelteKit"
: hasNuxt
? "Nuxt"
: ""
}, ${backend[0].toUpperCase() + backend.slice(1)}, tRPC, and more.
## Features
${generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend)}
## Getting Started
First, install the dependencies:
\`\`\`bash
${packageManager} install
\`\`\`
${generateDatabaseSetup(database, auth, packageManagerRunCmd, orm)}
Then, run the development server:
\`\`\`bash
${packageManagerRunCmd} dev
\`\`\`
${
hasTanstackRouter ||
hasReactRouter ||
hasNext ||
hasTanstackStart ||
hasSvelte ||
hasNuxt
? `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" : ""}
The API is running at [http://localhost:3000](http://localhost:3000).
${
addons.includes("pwa") && hasReactRouter
? "\n## PWA Support with React Router v7\n\nThere is a known compatibility issue between VitePWA and React Router v7.\nSee: https://github.com/vite-pwa/vite-plugin-pwa/issues/809\n"
: ""
}
## Project Structure
\`\`\`
${projectName}/
├── apps/
${
hasTanstackRouter ||
hasReactRouter ||
hasNext ||
hasTanstackStart ||
hasSvelte ||
hasNuxt
? `│ ├── web/ # Frontend application (${
hasTanstackRouter
? "React + TanStack Router"
: hasReactRouter
? "React + React Router"
: hasNext
? "Next.js"
: hasTanstackStart
? "React + TanStack Start"
: hasSvelte
? "SvelteKit"
: hasNuxt
? "Nuxt"
: ""
})\n`
: ""
}${
hasNative
? "│ ├── native/ # Mobile application (React Native, Expo)\n"
: ""
}${
addons.includes("starlight")
? "│ ├── docs/ # Documentation site (Astro Starlight)\n"
: ""
}│ └── server/ # Backend API (${
backend[0].toUpperCase() + backend.slice(1)
}, tRPC)
\`\`\`
## Available Scripts
${generateScriptsList(
packageManagerRunCmd,
database,
orm,
auth,
hasNative,
addons,
backend,
)}
`;
}
function generateFeaturesList(
database: ProjectDatabase,
auth: boolean,
addons: ProjectAddons[],
orm: ProjectOrm,
runtime: ProjectRuntime,
frontend: ProjectFrontend[],
backend: string,
): string {
const hasTanstackRouter = frontend.includes("tanstack-router");
const hasReactRouter = frontend.includes("react-router");
const hasNative = frontend.includes("native");
const hasNext = frontend.includes("next");
const hasTanstackStart = frontend.includes("tanstack-start");
const hasSvelte = frontend.includes("svelte");
const hasNuxt = frontend.includes("nuxt");
const addonsList = [
"- **TypeScript** - For type safety and improved developer experience",
];
if (hasTanstackRouter) {
addonsList.push(
"- **TanStack Router** - File-based routing with full type safety",
);
} else if (hasReactRouter) {
addonsList.push("- **React Router** - Declarative routing for React");
} else if (hasNext) {
addonsList.push("- **Next.js** - Full-stack React framework");
} else if (hasTanstackStart) {
addonsList.push(
"- **TanStack Start** - SSR framework with TanStack Router",
);
} else if (hasSvelte) {
addonsList.push("- **SvelteKit** - Web framework for building Svelte apps");
} else if (hasNuxt) {
addonsList.push("- **Nuxt** - The Intuitive Vue Framework");
}
if (hasNative) {
addonsList.push("- **React Native** - Build mobile apps using React");
addonsList.push("- **Expo** - Tools for React Native development");
}
addonsList.push(
"- **TailwindCSS** - Utility-first CSS for rapid UI development",
"- **shadcn/ui** - Reusable UI components",
);
if (backend === "hono") {
addonsList.push("- **Hono** - Lightweight, performant server framework");
} else if (backend === "express") {
addonsList.push("- **Express** - Fast, unopinionated web framework");
} else if (backend === "elysia") {
addonsList.push("- **Elysia** - Type-safe, high-performance framework");
} else if (backend === "next") {
addonsList.push("- **Next.js** - Full-stack React framework");
}
addonsList.push(
"- **tRPC** - End-to-end type-safe APIs",
`- **${runtime === "bun" ? "Bun" : "Node.js"}** - Runtime environment`,
);
if (database !== "none") {
addonsList.push(
`- **${
orm === "drizzle" ? "Drizzle" : "Prisma"
}** - TypeScript-first ORM`,
`- **${
database === "sqlite"
? "SQLite/Turso"
: database === "postgres"
? "PostgreSQL"
: database === "mysql"
? "MySQL"
: "MongoDB"
}** - Database engine`,
);
}
if (auth) {
addonsList.push(
"- **Authentication** - Email & password authentication with Better Auth",
);
}
for (const addon of addons) {
if (addon === "pwa") {
addonsList.push("- **PWA** - Progressive Web App support");
} else if (addon === "tauri") {
addonsList.push("- **Tauri** - Build native desktop applications");
} else if (addon === "biome") {
addonsList.push("- **Biome** - Linting and formatting");
} else if (addon === "husky") {
addonsList.push("- **Husky** - Git hooks for code quality");
} else if (addon === "starlight") {
addonsList.push("- **Starlight** - Documentation site with Astro");
}
}
return addonsList.join("\n");
}
function generateDatabaseSetup(
database: ProjectDatabase,
auth: boolean,
packageManagerRunCmd: string,
orm: ProjectOrm,
): string {
if (database === "none") {
return "";
}
let setup = "## Database Setup\n\n";
if (database === "sqlite") {
setup += `This project uses SQLite${
orm === "drizzle" ? " with Drizzle ORM" : " with Prisma"
}.
1. Start the local SQLite database:
\`\`\`bash
cd apps/server && ${packageManagerRunCmd} db:local
\`\`\`
2. Update your \`.env\` file in the \`apps/server\` directory with the appropriate connection details if needed.
`;
} else if (database === "postgres") {
setup += `This project uses PostgreSQL${
orm === "drizzle" ? " with Drizzle ORM" : " with Prisma"
}.
1. Make sure you have a PostgreSQL database set up.
2. Update your \`apps/server/.env\` file with your PostgreSQL connection details.
`;
} else if (database === "mysql") {
setup += `This project uses MySQL${
orm === "drizzle" ? " with Drizzle ORM" : " with Prisma"
}.
1. Make sure you have a MySQL database set up.
2. Update your \`apps/server/.env\` file with your MySQL connection details.
`;
} else if (database === "mongodb") {
setup += `This project uses MongoDB with Prisma ORM.
1. Make sure you have MongoDB set up.
2. Update your \`apps/server/.env\` file with your MongoDB connection URI.
`;
}
setup += `
${auth ? "3" : "3"}. ${
orm === "prisma"
? `Generate the Prisma client and push the schema:
\`\`\`bash
${packageManagerRunCmd} db:push
\`\`\``
: `Apply the schema to your database:
\`\`\`bash
${packageManagerRunCmd} db:push
\`\`\``
}
`;
return setup;
}
function generateScriptsList(
packageManagerRunCmd: string,
database: ProjectDatabase,
orm: ProjectOrm,
auth: boolean,
hasNative: boolean,
addons: ProjectAddons[],
backend: string,
): string {
let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
- \`${packageManagerRunCmd} build\`: Build all applications
- \`${packageManagerRunCmd} dev:web\`: Start only the web application
- \`${packageManagerRunCmd} dev:server\`: Start only the server
- \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
if (hasNative) {
scripts += `
- \`${packageManagerRunCmd} dev:native\`: Start the React Native/Expo development server`;
}
if (database !== "none") {
scripts += `
- \`${packageManagerRunCmd} db:push\`: Push schema changes to database
- \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
if (database === "sqlite" && orm === "drizzle") {
scripts += `
- \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
}
}
if (addons.includes("biome")) {
scripts += `
- \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
}
if (addons.includes("pwa")) {
scripts += `
- \`cd apps/web && ${packageManagerRunCmd} generate-pwa-assets\`: Generate PWA assets`;
}
if (addons.includes("tauri")) {
scripts += `
- \`cd apps/web && ${packageManagerRunCmd} desktop:dev\`: Start Tauri desktop app in development
- \`cd apps/web && ${packageManagerRunCmd} desktop:build\`: Build Tauri desktop app`;
}
if (addons.includes("starlight")) {
scripts += `
- \`cd apps/docs && ${packageManagerRunCmd} dev\`: Start documentation site
- \`cd apps/docs && ${packageManagerRunCmd} build\`: Build documentation site`;
}
return scripts;
}