setup turborepo and web app with fumadocs

This commit is contained in:
Aman Varshney
2025-02-12 00:45:40 +05:30
parent 8a77febd7e
commit 8b730b9adc
38 changed files with 1689 additions and 140 deletions

2
apps/cli/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/node_modules
/dist

72
apps/cli/README.md Normal file
View File

@@ -0,0 +1,72 @@
# Create Better-T-Stack
A CLI tool to scaffold Better-T Stack projects with best practices and modern tooling.
## Features
- 🚀 Quick project setup with one command
- 📦 TypeScript/JavaScript support
- 🗄️ Database options (libSQL/PostgreSQL)
- 🔒 Optional authentication setup
- 🐳 Docker configuration
- 🔄 GitHub Actions workflows
- 🎯 SEO optimization
## Quick Start
```bash
# Using npm
npx create-better-t my-app
# Using bun
bunx create-better-t my-app
```
Just follow the interactive prompts!
## Options
```bash
Usage: create-better-t [project-directory] [options]
Options:
--typescript Use TypeScript (default)
--javascript Use JavaScript
--git Initialize git repository (default)
--no-git Skip git initialization
-h, --help Display help
```
## Project Structure
The generated project follows the Better-T Stack architecture:
- Built with Bun
- Type-safe database with DrizzleORM
- Simple authentication system
- Modern development practices
## Development
To contribute to this CLI:
```bash
# Clone the repository
git clone https://github.com/your-username/better-t-stack-cli.git
# Install dependencies
bun install
# Start development
bun dev
# Build
bun run build
```
## License
MIT
## Credits
Developed by Nitish Singh & Aman Varshney Built on top of the Better-T Stack by [Aman Varshney](https://github.com/AmanVarshney01/Better-T-Stack)

35
apps/cli/package.json Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "create-better-t-stack",
"version": "1.0.0",
"description": "CLI tool to scaffold Better-T Stack projects",
"type": "module",
"bin": {
"create-better-t-stack": "./dist/index.js"
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"check-types": "tsc --noEmit",
"check": "biome check --write .",
"test": "vitest run",
"prepublishOnly": "npm run build"
},
"files": ["dist", "templates"],
"dependencies": {
"@inquirer/prompts": "^7.3.1",
"chalk": "^5.3.0",
"commander": "^13.1.0",
"execa": "^8.0.1",
"fs-extra": "^11.2.0",
"gradient-string": "^3.0.0",
"ora": "^7.0.1"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/inquirer": "^9.0.7",
"@types/node": "^20.10.5",
"tsup": "^8.0.1",
"typescript": "^5.3.3",
"vitest": "^1.1.0"
}
}

21
apps/cli/src/consts.ts Normal file
View File

@@ -0,0 +1,21 @@
export const TITLE_TEXT = `
╔════════════════════════════════════════════════════════════╗
║ ║
║ ██████╗ ███████╗████████╗████████╗███████╗██████╗ ║
║ ██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗ ║
║ ██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝ ║
║ ██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗ ║
║ ██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║ ║
║ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ║
║ ║
║ ████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗ ║
║ ╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝ ║
║ ██║ ███████╗ ██║ ███████║██║ █████╔╝ ║
║ ██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗ ║
║ ██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗ ║
║ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ║
║ ║
║ The Modern Full-Stack Framework ║
║ ║
╚════════════════════════════════════════════════════════════╝
`;

View File

@@ -0,0 +1,47 @@
import path from "node:path";
import { execa } from "execa";
import fs from "fs-extra";
import ora from "ora";
import { setupTurso } from "./helpers/db-setup";
import type { ProjectOptions } from "./types";
export async function createProject(options: ProjectOptions) {
const spinner = ora("Creating project directory...").start();
const projectDir = path.resolve(process.cwd(), options.projectName);
try {
await fs.ensureDir(projectDir);
spinner.succeed();
spinner.start("Cloning template repository...");
await execa("git", [
"degit",
"https://github.com/AmanVarshney01/Better-T-Stack.git",
projectDir,
]);
spinner.succeed();
if (options.git) {
spinner.start("Initializing git repository...");
await execa("git", ["init"], { cwd: projectDir });
spinner.succeed();
}
spinner.start("Installing dependencies...");
await execa("bun", ["install"], { cwd: projectDir });
spinner.succeed();
if (options.database === "libsql") {
await setupTurso(projectDir);
}
console.log("\n✨ Project created successfully!\n");
console.log("Next steps:");
console.log(` cd ${options.projectName}`);
console.log(" bun dev");
} catch (error) {
spinner.fail("Failed to create project");
console.error(error);
process.exit(1);
}
}

View File

@@ -0,0 +1,137 @@
import os from "node:os";
import path from "node:path";
import { confirm, input } from "@inquirer/prompts";
import { execa } from "execa";
import fs from "fs-extra";
import ora, { type Ora } from "ora";
async function isTursoInstalled() {
try {
await execa("turso", ["--version"]);
return true;
} catch {
return false;
}
}
async function isTursoLoggedIn() {
try {
await execa("turso", ["auth", "whoami"]);
return true;
} catch {
return false;
}
}
async function installTursoCLI(isMac: boolean, spinner: Ora) {
try {
if (await isTursoLoggedIn()) {
spinner.succeed("Turso CLI already logged in!");
return;
}
spinner.start("Installing Turso CLI...");
if (isMac) {
await execa("brew", ["install", "tursodatabase/tap/turso"]);
} else {
const installScript = await execa("curl", [
"-sSfL",
"https://get.tur.so/install.sh",
]);
await execa("bash", [], { input: installScript.stdout });
}
spinner.succeed("Turso CLI installed successfully!");
spinner.start("Logging in to Turso...");
await execa("turso", ["auth", "login"]);
spinner.succeed("Logged in to Turso!");
} catch (error) {
console.error(error);
spinner.fail(
"Failed to install Turso CLI. Proceeding with manual setup...",
);
}
}
export async function setupTurso(projectDir: string) {
const spinner = ora();
const platform = os.platform();
const isMac = platform === "darwin";
let canInstallCLI = platform !== "win32";
let installTurso = true;
const isCliInstalled = await isTursoInstalled();
if (canInstallCLI && !isCliInstalled) {
installTurso = await confirm({
message: "Would you like to install Turso CLI?",
default: true,
});
}
canInstallCLI = canInstallCLI && installTurso;
if (canInstallCLI) {
try {
await installTursoCLI(isMac, spinner);
const defaultDbName = path.basename(projectDir);
const dbName = await input({
message: `Enter database name (default: ${defaultDbName}):`,
default: defaultDbName,
});
spinner.start(`Creating Turso database "${dbName}"...`);
await execa("turso", ["db", "create", dbName]);
const { stdout: dbUrl } = await execa("turso", [
"db",
"show",
dbName,
"--url",
]);
const { stdout: authToken } = await execa("turso", [
"db",
"tokens",
"create",
dbName,
]);
const envPath = path.join(projectDir, "packages/server", ".env");
const envContent = `TURSO_DATABASE_URL="${dbUrl.trim()}"
TURSO_AUTH_TOKEN="${authToken.trim()}"`;
await fs.writeFile(envPath, envContent);
spinner.succeed("Turso database configured successfully!");
return;
} catch (error) {
console.error(error);
spinner.fail(
"Failed to install Turso CLI. Proceeding with manual setup...",
);
installTurso = false;
}
}
if (!installTurso) {
const envPath = path.join(projectDir, "packages/server", ".env");
const envContent = `TURSO_DATABASE_URL=
TURSO_AUTH_TOKEN=`;
await fs.writeFile(envPath, envContent);
console.log("\n📝 Manual Turso Setup Instructions:");
console.log("1. Visit https://turso.tech and create an account");
console.log("2. Create a new database from the dashboard");
console.log("3. Get your database URL and authentication token");
console.log(
"4. Add these credentials to the .env file in your project root",
);
console.log("\nThe .env file has been created with placeholder variables:");
console.log("TURSO_DATABASE_URL=your_database_url");
console.log("TURSO_AUTH_TOKEN=your_auth_token");
}
}

81
apps/cli/src/index.ts Normal file
View File

@@ -0,0 +1,81 @@
import { checkbox, confirm, input, select } from "@inquirer/prompts";
import chalk from "chalk";
import { Command } from "commander";
import { createProject } from "./create-project";
import { renderTitle } from "./render-title";
import type { ProjectDatabase, ProjectFeature } from "./types";
const program = new Command();
async function main() {
renderTitle();
console.log(chalk.bold("\n🚀 Creating a new Better-T Stack project...\n"));
const projectName = await input({
message: "Project name:",
default: "my-better-t-app",
});
const database = await select<ProjectDatabase>({
message: chalk.cyan("Select database:"),
choices: [
{
value: "libsql",
name: "libSQL",
description: chalk.dim(
"(Recommended) - Turso's embedded SQLite database",
),
},
{
value: "postgres",
name: "PostgreSQL",
description: chalk.dim("Traditional relational database"),
},
],
});
const auth = await confirm({
message: "Add authentication with Better-Auth?",
default: true,
});
const features = await checkbox<ProjectFeature>({
message: chalk.cyan("Select additional features:"),
choices: [
{
value: "docker",
name: "Docker setup",
description: chalk.dim("Containerize your application"),
},
{
value: "github-actions",
name: "GitHub Actions",
description: chalk.dim("CI/CD workflows"),
},
{
value: "SEO",
name: "Basic SEO setup",
description: chalk.dim("Search engine optimization configuration"),
},
],
});
const projectOptions = {
projectName,
git: true,
database,
auth,
features,
};
await createProject(projectOptions);
}
program
.name("create-better-t-stack")
.description("Create a new Better-T Stack project")
.version("1.0.0")
.action(main);
program.parse();

View File

@@ -0,0 +1,23 @@
import gradient from "gradient-string";
import { TITLE_TEXT } from "./consts";
const catppuccinTheme = {
rosewater: "#F5E0DC",
flamingo: "#F2CDCD",
pink: "#F5C2E7",
mauve: "#CBA6F7",
red: "#F38BA8",
maroon: "#E78284",
peach: "#FAB387",
yellow: "#F9E2AF",
green: "#A6E3A1",
teal: "#94E2D5",
sky: "#89DCEB",
sapphire: "#74C7EC",
lavender: "#B4BEFE",
};
export const renderTitle = () => {
const catppuccinGradient = gradient(Object.values(catppuccinTheme));
console.log(catppuccinGradient.multiline(TITLE_TEXT));
};

10
apps/cli/src/types.ts Normal file
View File

@@ -0,0 +1,10 @@
export type ProjectFeature = "docker" | "github-actions" | "SEO";
export type ProjectDatabase = "libsql" | "postgres";
export type ProjectOptions = {
projectName: string;
git: boolean;
database: ProjectDatabase;
auth: boolean;
features: ProjectFeature[];
};

14
apps/cli/tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}

14
apps/cli/tsup.config.ts Normal file
View File

@@ -0,0 +1,14 @@
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm"],
clean: true,
dts: true,
shims: true,
splitting: false,
outDir: "dist",
banner: {
js: "#!/usr/bin/env node",
},
});