mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
Update cli prompts
This commit is contained in:
5
.changeset/ready-bags-prove.md
Normal file
5
.changeset/ready-bags-prove.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"create-better-t-stack": patch
|
||||
---
|
||||
|
||||
Update CLI prompts
|
||||
@@ -50,8 +50,7 @@ Options:
|
||||
--auth Include authentication
|
||||
--no-auth Exclude authentication
|
||||
--frontend <types...> Frontend types (web, native, none)
|
||||
--addons <types...> Additional addons (pwa, tauri, biome, husky)
|
||||
--no-addons Skip all additional addons
|
||||
--addons <types...> Additional addons (pwa, tauri, biome, husky, none)
|
||||
--examples <types...> Examples to include (todo, ai)
|
||||
--no-examples Skip all examples
|
||||
--git Initialize git repository
|
||||
|
||||
@@ -7,11 +7,35 @@
|
||||
"bin": {
|
||||
"create-better-t-stack": "dist/index.js"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"template"
|
||||
"files": ["dist", "template"],
|
||||
"keywords": [
|
||||
"typescript",
|
||||
"scaffold",
|
||||
"generator",
|
||||
"boilerplate",
|
||||
"starter",
|
||||
"cli",
|
||||
"create-app",
|
||||
"t3-stack",
|
||||
"turborepo",
|
||||
"trpc",
|
||||
"monorepo",
|
||||
"fullstack",
|
||||
"type-safety",
|
||||
"react",
|
||||
"react-native",
|
||||
"expo",
|
||||
"hono",
|
||||
"elysia",
|
||||
"drizzle",
|
||||
"prisma",
|
||||
"tanstack",
|
||||
"tailwind",
|
||||
"shadcn",
|
||||
"pwa",
|
||||
"tauri",
|
||||
"biome"
|
||||
],
|
||||
"keywords": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/better-t-stack/create-better-t-stack.git",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import path from "node:path";
|
||||
import { log } from "@clack/prompts";
|
||||
import { $, execa } from "execa";
|
||||
import fs from "fs-extra";
|
||||
import pc from "picocolors";
|
||||
import { PKG_ROOT } from "../constants";
|
||||
import type { ProjectConfig } from "../types";
|
||||
|
||||
@@ -79,7 +81,26 @@ export async function initializeGit(
|
||||
projectDir: string,
|
||||
useGit: boolean,
|
||||
): Promise<void> {
|
||||
if (useGit) {
|
||||
await $({ cwd: projectDir })`git init`;
|
||||
if (!useGit) return;
|
||||
|
||||
const gitVersionResult = await $({
|
||||
cwd: projectDir,
|
||||
reject: false,
|
||||
stderr: "pipe",
|
||||
})`git --version`;
|
||||
|
||||
if (gitVersionResult.exitCode !== 0) {
|
||||
log.warn(pc.yellow("Git is not installed"));
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await $({
|
||||
cwd: projectDir,
|
||||
reject: false,
|
||||
stderr: "pipe",
|
||||
})`git init`;
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
throw new Error(`Git initialization failed: ${result.stderr}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,8 @@ async function main() {
|
||||
.option("--frontend <types...>", "Frontend types (web, native, none)")
|
||||
.option(
|
||||
"--addons <types...>",
|
||||
"Additional addons (pwa, tauri, biome, husky)",
|
||||
"Additional addons (pwa, tauri, biome, husky, none)",
|
||||
)
|
||||
.option("--no-addons", "Skip all additional addons")
|
||||
.option("--examples <types...>", "Examples to include (todo, ai)")
|
||||
.option("--no-examples", "Skip all examples")
|
||||
.option("--git", "Initialize git repository")
|
||||
@@ -285,8 +284,12 @@ function validateOptions(options: CLIOptions): void {
|
||||
}
|
||||
}
|
||||
|
||||
if (options.addons && options.addons.length > 0) {
|
||||
const validAddons = ["pwa", "tauri", "biome", "husky"];
|
||||
if (
|
||||
options.addons &&
|
||||
Array.isArray(options.addons) &&
|
||||
options.addons.length > 0
|
||||
) {
|
||||
const validAddons = ["pwa", "tauri", "biome", "husky", "none"];
|
||||
const invalidAddons = options.addons.filter(
|
||||
(addon: string) => !validAddons.includes(addon),
|
||||
);
|
||||
@@ -300,6 +303,11 @@ function validateOptions(options: CLIOptions): void {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (options.addons.includes("none") && options.addons.length > 1) {
|
||||
cancel(pc.red(`Cannot combine 'none' with other addons.`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const webSpecificAddons = ["pwa", "tauri"];
|
||||
const hasWebSpecificAddons = options.addons.some((addon) =>
|
||||
webSpecificAddons.includes(addon),
|
||||
@@ -382,10 +390,10 @@ function processFlags(
|
||||
}
|
||||
|
||||
let addons: ProjectAddons[] | undefined;
|
||||
if ("addons" in options) {
|
||||
if (options.addons === undefined) {
|
||||
if (options.addons && Array.isArray(options.addons)) {
|
||||
if (options.addons.includes("none")) {
|
||||
addons = [];
|
||||
} else if (options.addons) {
|
||||
} else {
|
||||
addons = options.addons.filter(
|
||||
(addon): addon is ProjectAddons =>
|
||||
addon === "pwa" ||
|
||||
|
||||
@@ -44,7 +44,7 @@ export async function getAddonsChoice(
|
||||
);
|
||||
|
||||
const response = await multiselect<ProjectAddons>({
|
||||
message: "Which Addons would you like to add?",
|
||||
message: "Select addons",
|
||||
options,
|
||||
initialValues,
|
||||
required: false,
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function getAuthChoice(
|
||||
if (auth !== undefined) return auth;
|
||||
|
||||
const response = await confirm({
|
||||
message: "Would you like to add authentication with Better-Auth?",
|
||||
message: "Add authentication with Better-Auth?",
|
||||
initialValue: DEFAULT_CONFIG.auth,
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function getBackendFrameworkChoice(
|
||||
if (backendFramework !== undefined) return backendFramework;
|
||||
|
||||
const response = await select<ProjectBackend>({
|
||||
message: "Which backend framework would you like to use?",
|
||||
message: "Select backend framework",
|
||||
options: [
|
||||
{
|
||||
value: "hono",
|
||||
@@ -19,7 +19,7 @@ export async function getBackendFrameworkChoice(
|
||||
{
|
||||
value: "elysia",
|
||||
label: "Elysia",
|
||||
hint: "TypeScript framework with end-to-end type safety)",
|
||||
hint: "Ergonomic web framework for building backend servers",
|
||||
},
|
||||
],
|
||||
initialValue: DEFAULT_CONFIG.backend,
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function getDatabaseChoice(
|
||||
if (database !== undefined) return database;
|
||||
|
||||
const response = await select<ProjectDatabase>({
|
||||
message: "Which database would you like to use?",
|
||||
message: "Select database",
|
||||
options: [
|
||||
{
|
||||
value: "none",
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function getExamplesChoice(
|
||||
|
||||
if (backend === "elysia") {
|
||||
response = await multiselect<ProjectExamples>({
|
||||
message: "Which examples would you like to include?",
|
||||
message: "Include examples",
|
||||
options: [
|
||||
{
|
||||
value: "todo",
|
||||
@@ -40,7 +40,7 @@ export async function getExamplesChoice(
|
||||
|
||||
if (backend === "hono") {
|
||||
response = await multiselect<ProjectExamples>({
|
||||
message: "Which examples would you like to include?",
|
||||
message: "Include examples",
|
||||
options: [
|
||||
{
|
||||
value: "todo",
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function getFrontendChoice(
|
||||
if (frontendOptions !== undefined) return frontendOptions;
|
||||
|
||||
const response = await multiselect<ProjectFrontend>({
|
||||
message: "Which frontend applications would you like to create?",
|
||||
message: "Choose frontends",
|
||||
options: [
|
||||
{
|
||||
value: "web",
|
||||
|
||||
@@ -6,7 +6,7 @@ export async function getGitChoice(git?: boolean): Promise<boolean> {
|
||||
if (git !== undefined) return git;
|
||||
|
||||
const response = await confirm({
|
||||
message: "Initialize a new git repository?",
|
||||
message: "Initialize git repository?",
|
||||
initialValue: DEFAULT_CONFIG.git,
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function getNoInstallChoice(
|
||||
if (noInstall !== undefined) return noInstall;
|
||||
|
||||
const response = await confirm({
|
||||
message: "Do you want to install project dependencies?",
|
||||
message: "Install dependencies?",
|
||||
initialValue: !DEFAULT_CONFIG.noInstall,
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export async function getORMChoice(
|
||||
if (orm !== undefined) return orm;
|
||||
|
||||
const response = await select<ProjectOrm>({
|
||||
message: "Which ORM would you like to use?",
|
||||
message: "Select ORM",
|
||||
options: [
|
||||
{
|
||||
value: "drizzle",
|
||||
|
||||
@@ -11,7 +11,7 @@ export async function getPackageManagerChoice(
|
||||
const detectedPackageManager = getUserPkgManager();
|
||||
|
||||
const response = await select<ProjectPackageManager>({
|
||||
message: "Which package manager do you want to use?",
|
||||
message: "Choose package manager",
|
||||
options: [
|
||||
{ value: "npm", label: "npm", hint: "Node Package Manager" },
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function getRuntimeChoice(
|
||||
if (runtime !== undefined) return runtime;
|
||||
|
||||
const response = await select<ProjectRuntime>({
|
||||
message: "Which runtime would you like to use?",
|
||||
message: "Select runtime",
|
||||
options: [
|
||||
{
|
||||
value: "bun",
|
||||
|
||||
@@ -6,7 +6,7 @@ export async function getTursoSetupChoice(turso?: boolean): Promise<boolean> {
|
||||
if (turso !== undefined) return turso;
|
||||
|
||||
const response = await confirm({
|
||||
message: "Set up a Turso database for this project?",
|
||||
message: "Set up Turso database?",
|
||||
initialValue: DEFAULT_CONFIG.turso,
|
||||
});
|
||||
|
||||
|
||||
@@ -18,9 +18,7 @@ export function generateReproducibleCommand(config: ProjectConfig): string {
|
||||
}
|
||||
|
||||
flags.push(config.auth ? "--auth" : "--no-auth");
|
||||
|
||||
flags.push(config.git ? "--git" : "--no-git");
|
||||
|
||||
flags.push(config.noInstall ? "--no-install" : "--install");
|
||||
|
||||
if (config.runtime) {
|
||||
@@ -38,7 +36,7 @@ export function generateReproducibleCommand(config: ProjectConfig): string {
|
||||
if (config.addons && config.addons.length > 0) {
|
||||
flags.push(`--addons ${config.addons.join(" ")}`);
|
||||
} else {
|
||||
flags.push("--no-addons");
|
||||
flags.push("--addons none");
|
||||
}
|
||||
|
||||
if (config.examples && config.examples.length > 0) {
|
||||
@@ -55,7 +53,7 @@ export function generateReproducibleCommand(config: ProjectConfig): string {
|
||||
const pkgManager = config.packageManager;
|
||||
|
||||
if (pkgManager === "npm") {
|
||||
baseCommand = "npm create better-t-stack@latest";
|
||||
baseCommand = "npx create-better-t-stack@latest";
|
||||
} else if (pkgManager === "pnpm") {
|
||||
baseCommand = "pnpm create better-t-stack@latest";
|
||||
} else if (pkgManager === "bun") {
|
||||
|
||||
@@ -243,7 +243,7 @@ const TECH_OPTIONS = {
|
||||
{
|
||||
id: "ai",
|
||||
name: "AI Example",
|
||||
description: "AI integration example",
|
||||
description: "AI integration example using AI SDK",
|
||||
icon: "🤖",
|
||||
color: "from-purple-500 to-purple-700",
|
||||
default: false,
|
||||
@@ -441,6 +441,8 @@ const StackArchitect = () => {
|
||||
|
||||
if (stackState.addons.length > 0) {
|
||||
flags.push(`--addons ${stackState.addons.join(" ")}`);
|
||||
} else {
|
||||
flags.push("--addons none");
|
||||
}
|
||||
|
||||
if (stackState.examples.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user