mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
setup turborepo and web app with fumadocs
This commit is contained in:
21
apps/cli/src/consts.ts
Normal file
21
apps/cli/src/consts.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export const TITLE_TEXT = `
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ ██████╗ ███████╗████████╗████████╗███████╗██████╗ ║
|
||||
║ ██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗ ║
|
||||
║ ██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝ ║
|
||||
║ ██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗ ║
|
||||
║ ██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║ ║
|
||||
║ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ║
|
||||
║ ║
|
||||
║ ████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗ ║
|
||||
║ ╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝ ║
|
||||
║ ██║ ███████╗ ██║ ███████║██║ █████╔╝ ║
|
||||
║ ██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗ ║
|
||||
║ ██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗ ║
|
||||
║ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ║
|
||||
║ ║
|
||||
║ The Modern Full-Stack Framework ║
|
||||
║ ║
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
`;
|
||||
47
apps/cli/src/create-project.ts
Normal file
47
apps/cli/src/create-project.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
137
apps/cli/src/helpers/db-setup.ts
Normal file
137
apps/cli/src/helpers/db-setup.ts
Normal 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
81
apps/cli/src/index.ts
Normal 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();
|
||||
23
apps/cli/src/render-title.ts
Normal file
23
apps/cli/src/render-title.ts
Normal 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
10
apps/cli/src/types.ts
Normal 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[];
|
||||
};
|
||||
Reference in New Issue
Block a user