From f804a9efda8edddce60376b4a8d521b235852552 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Thu, 20 Feb 2025 19:14:28 +0530 Subject: [PATCH] organize code --- .changeset/loud-socks-reflect.md | 5 + apps/cli/src/{consts.ts => constants.ts} | 2 +- apps/cli/src/helpers/create-project.ts | 2 + apps/cli/src/index.ts | 317 +++--------------- apps/cli/src/prompts/auth.ts | 23 ++ apps/cli/src/prompts/config-prompts.ts | 62 ++++ apps/cli/src/prompts/database.ts | 38 +++ apps/cli/src/prompts/features.ts | 38 +++ apps/cli/src/prompts/git.ts | 19 ++ apps/cli/src/prompts/orm.ts | 30 ++ apps/cli/src/prompts/package-manager.ts | 52 +++ apps/cli/src/prompts/project-name.ts | 95 ++++++ apps/cli/src/types.ts | 4 +- apps/cli/src/utils/display-config.ts | 32 ++ .../utils/generate-reproducible-command.ts | 12 +- apps/cli/src/utils/get-version.ts | 2 +- 16 files changed, 451 insertions(+), 282 deletions(-) create mode 100644 .changeset/loud-socks-reflect.md rename apps/cli/src/{consts.ts => constants.ts} (100%) create mode 100644 apps/cli/src/prompts/auth.ts create mode 100644 apps/cli/src/prompts/config-prompts.ts create mode 100644 apps/cli/src/prompts/database.ts create mode 100644 apps/cli/src/prompts/features.ts create mode 100644 apps/cli/src/prompts/git.ts create mode 100644 apps/cli/src/prompts/orm.ts create mode 100644 apps/cli/src/prompts/package-manager.ts create mode 100644 apps/cli/src/prompts/project-name.ts create mode 100644 apps/cli/src/utils/display-config.ts diff --git a/.changeset/loud-socks-reflect.md b/.changeset/loud-socks-reflect.md new file mode 100644 index 0000000..7b49a5f --- /dev/null +++ b/.changeset/loud-socks-reflect.md @@ -0,0 +1,5 @@ +--- +"create-better-t-stack": minor +--- + +add flags and prompt for orm, add none option in database diff --git a/apps/cli/src/consts.ts b/apps/cli/src/constants.ts similarity index 100% rename from apps/cli/src/consts.ts rename to apps/cli/src/constants.ts index 19a3151..5af5846 100644 --- a/apps/cli/src/consts.ts +++ b/apps/cli/src/constants.ts @@ -9,9 +9,9 @@ export const PKG_ROOT = path.join(distPath, "../"); export const DEFAULT_CONFIG: ProjectConfig = { projectName: "my-better-t-app", database: "sqlite", + orm: "drizzle", auth: true, features: [], git: true, packageManager: "npm", - orm: "drizzle", }; diff --git a/apps/cli/src/helpers/create-project.ts b/apps/cli/src/helpers/create-project.ts index a50e599..e0280bc 100644 --- a/apps/cli/src/helpers/create-project.ts +++ b/apps/cli/src/helpers/create-project.ts @@ -52,6 +52,8 @@ export async function createProject(options: ProjectConfig) { if (options.database === "sqlite") { await setupTurso(projectDir); + } else if (options.database === "postgres") { + // Handle postgres setup } const installDepsResponse = await confirm({ diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index dc7d86b..6def2a5 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -1,30 +1,12 @@ -import path from "node:path"; -import { - cancel, - confirm, - group, - intro, - log, - multiselect, - outro, - select, - spinner, - text, -} from "@clack/prompts"; +import { cancel, intro, log, outro, spinner } from "@clack/prompts"; import { Command } from "commander"; -import fs from "fs-extra"; import pc from "picocolors"; -import { DEFAULT_CONFIG } from "./consts"; +import { DEFAULT_CONFIG } from "./constants"; import { createProject } from "./helpers/create-project"; -import type { - PackageManager, - ProjectConfig, - ProjectDatabase, - ProjectFeature, - ProjectORM, -} from "./types"; +import { gatherConfig } from "./prompts/config-prompts"; +import type { PackageManager, ProjectConfig, ProjectFeature } from "./types"; +import { displayConfig } from "./utils/display-config"; import { generateReproducibleCommand } from "./utils/generate-reproducible-command"; -import { getUserPkgManager } from "./utils/get-package-manager"; import { getVersion } from "./utils/get-version"; import { renderTitle } from "./utils/render-title"; @@ -35,215 +17,6 @@ process.on("SIGINT", () => { const program = new Command(); -async function gatherConfig( - flags: Partial, -): Promise { - const result = await group( - { - projectName: async () => { - if (flags.projectName) return flags.projectName; - let isValid = false; - let projectName: string | symbol = ""; - let defaultName = DEFAULT_CONFIG.projectName; - let counter = 1; - - while (fs.pathExistsSync(path.resolve(process.cwd(), defaultName))) { - defaultName = `${DEFAULT_CONFIG.projectName}-${counter}`; - counter++; - } - - while (!isValid) { - const response = await text({ - message: "What is your project named? (directory name or path)", - placeholder: defaultName, - initialValue: flags.projectName, - defaultValue: defaultName, - validate: (value) => { - const nameToUse = value.trim() || defaultName; - const projectDir = path.resolve(process.cwd(), nameToUse); - - if (fs.pathExistsSync(projectDir)) { - const dirContents = fs.readdirSync(projectDir); - if (dirContents.length > 0) { - return `Directory "${nameToUse}" already exists and is not empty. Please choose a different name.`; - } - } - - isValid = true; - return undefined; - }, - }); - - if (typeof response === "symbol") { - cancel(pc.red("Operation cancelled.")); - process.exit(0); - } - - projectName = response || defaultName; - } - - return projectName as string; - }, - database: () => - flags.database !== undefined - ? Promise.resolve(flags.database) - : select({ - message: "Which database would you like to use?", - options: [ - { - value: "sqlite", - label: "SQLite", - hint: "by Turso (recommended)", - }, - { - value: "postgres", - label: "PostgreSQL", - hint: "Traditional relational database", - }, - ], - }), - orm: () => - flags.orm !== undefined - ? Promise.resolve(flags.orm) - : select({ - message: "Which ORM would you like to use?", - options: [ - { - value: "drizzle", - label: "Drizzle", - hint: "Type-safe, lightweight ORM (recommended)", - }, - // { - // value: "prisma", - // label: "Prisma (coming soon)", - // hint: "Feature-rich ORM with great DX", - // }, - ], - initialValue: "drizzle", - }), - auth: () => - flags.auth !== undefined - ? Promise.resolve(flags.auth) - : confirm({ - message: "Would you like to add authentication with Better-Auth?", - initialValue: DEFAULT_CONFIG.auth, - }), - features: () => - flags.features !== undefined - ? Promise.resolve(flags.features) - : multiselect({ - message: "Which features would you like to add?", - options: [ - { - value: "docker", - label: "Docker setup", - hint: "Containerize your application", - }, - { - value: "github-actions", - label: "GitHub Actions", - hint: "CI/CD workflows", - }, - { - value: "SEO", - label: "Basic SEO setup", - hint: "Search engine optimization configuration", - }, - ], - required: false, - }), - git: () => - flags.git !== undefined - ? Promise.resolve(flags.git) - : confirm({ - message: "Initialize a new git repository?", - initialValue: DEFAULT_CONFIG.git, - }), - packageManager: async () => { - if (flags.packageManager !== undefined) { - return flags.packageManager; - } - const detectedPackageManager = getUserPkgManager(); - - const useDetected = await confirm({ - message: `Use ${detectedPackageManager} as your package manager?`, - }); - - if (useDetected) return detectedPackageManager; - - return select({ - message: "Which package manager would you like to use?", - options: [ - { value: "npm", label: "npm", hint: "Node Package Manager" }, - { - value: "pnpm", - label: "pnpm", - hint: "Fast, disk space efficient package manager", - }, - { - value: "yarn", - label: "yarn", - hint: "Fast, reliable, and secure dependency management", - }, - { - value: "bun", - label: "bun", - hint: "All-in-one JavaScript runtime & toolkit (recommended)", - }, - ], - initialValue: "bun", - }); - }, - }, - { - onCancel: () => { - cancel(pc.red("Operation cancelled.")); - process.exit(0); - }, - }, - ); - - return { - projectName: result.projectName ?? DEFAULT_CONFIG.projectName, - database: result.database ?? DEFAULT_CONFIG.database, - orm: result.orm ?? DEFAULT_CONFIG.orm, - auth: result.auth ?? DEFAULT_CONFIG.auth, - features: result.features ?? DEFAULT_CONFIG.features, - git: result.git ?? DEFAULT_CONFIG.git, - packageManager: result.packageManager ?? DEFAULT_CONFIG.packageManager, - }; -} - -function displayConfig(config: Partial) { - const configDisplay = []; - - if (config.projectName) { - configDisplay.push(`${pc.blue("Project Name:")} ${config.projectName}`); - } - if (config.database) { - configDisplay.push(`${pc.blue("Database:")} ${config.database}`); - } - if (config.orm) { - configDisplay.push(`${pc.blue("ORM:")} ${config.orm}`); - } - if (config.auth !== undefined) { - configDisplay.push(`${pc.blue("Authentication:")} ${config.auth}`); - } - if (config.features?.length) { - configDisplay.push(`${pc.blue("Features:")} ${config.features.join(", ")}`); - } - if (config.git !== undefined) { - configDisplay.push(`${pc.blue("Git Init:")} ${config.git}`); - } - if (config.packageManager) { - configDisplay.push( - `${pc.blue("Package Manager:")} ${config.packageManager}`, - ); - } - - return configDisplay.join("\n"); -} - async function main() { const s = spinner(); try { @@ -256,6 +29,7 @@ async function main() { .version(getVersion()) .argument("[project-directory]", "Project name/directory") .option("-y, --yes", "Use default configuration") + .option("--no-database", "Skip database setup") .option("--sqlite", "Use SQLite database") .option("--postgres", "Use PostgreSQL database") .option("--auth", "Include authentication") @@ -277,38 +51,28 @@ async function main() { const projectDirectory = program.args[0]; const flagConfig: Partial = { - projectName: projectDirectory || undefined, - database: options.sqlite - ? "sqlite" - : options.postgres - ? "postgres" - : undefined, - orm: options.drizzle ? "drizzle" : options.prisma ? "prisma" : undefined, - auth: "auth" in options ? options.auth : undefined, - packageManager: options.npm - ? "npm" - : options.pnpm - ? "pnpm" - : options.yarn - ? "yarn" - : options.bun - ? "bun" - : undefined, - git: "git" in options ? options.git : undefined, - features: - options.docker || options.githubActions || options.seo - ? ([ - ...(options.docker ? ["docker"] : []), - ...(options.githubActions ? ["github-actions"] : []), - ...(options.seo ? ["SEO"] : []), - ] as ProjectFeature[]) - : undefined, + ...(projectDirectory && { projectName: projectDirectory }), + ...(options.database === false && { database: "none" }), + ...(options.sqlite && { database: "sqlite" }), + ...(options.postgres && { database: "postgres" }), + ...(options.drizzle && { orm: "drizzle" }), + ...(options.prisma && { orm: "prisma" }), + ...("auth" in options && { auth: options.auth }), + ...(options.npm && { packageManager: "npm" }), + ...(options.pnpm && { packageManager: "pnpm" }), + ...(options.yarn && { packageManager: "yarn" }), + ...(options.bun && { packageManager: "bun" }), + ...("git" in options && { git: options.git }), + ...((options.docker || options.githubActions || options.seo) && { + features: [ + ...(options.docker ? ["docker"] : []), + ...(options.githubActions ? ["github-actions"] : []), + ...(options.seo ? ["SEO"] : []), + ] as ProjectFeature[], + }), }; - if ( - !options.yes && - Object.values(flagConfig).some((v) => v !== undefined) - ) { + if (!options.yes && Object.keys(flagConfig).length > 0) { log.info(pc.yellow("Using these pre-selected options:")); log.message(displayConfig(flagConfig)); log.message(""); @@ -317,23 +81,26 @@ async function main() { const config = options.yes ? { ...DEFAULT_CONFIG, - yes: true, projectName: projectDirectory ?? DEFAULT_CONFIG.projectName, - database: options.database ?? DEFAULT_CONFIG.database, - orm: options.drizzle - ? "drizzle" - : options.prisma - ? "prisma" - : DEFAULT_CONFIG.orm, // Add this line + database: + options.database === false + ? "none" + : (options.database ?? DEFAULT_CONFIG.database), + orm: + options.database === false + ? "none" + : options.drizzle + ? "drizzle" + : options.prisma + ? "prisma" + : DEFAULT_CONFIG.orm, auth: options.auth ?? DEFAULT_CONFIG.auth, git: options.git ?? DEFAULT_CONFIG.git, packageManager: - options.packageManager ?? DEFAULT_CONFIG.packageManager, - features: [ - ...(options.docker ? ["docker"] : []), - ...(options.githubActions ? ["github-actions"] : []), - ...(options.seo ? ["SEO"] : []), - ] as ProjectFeature[], + flagConfig.packageManager ?? DEFAULT_CONFIG.packageManager, + features: flagConfig.features?.length + ? flagConfig.features + : DEFAULT_CONFIG.features, } : await gatherConfig(flagConfig); diff --git a/apps/cli/src/prompts/auth.ts b/apps/cli/src/prompts/auth.ts new file mode 100644 index 0000000..bd270a7 --- /dev/null +++ b/apps/cli/src/prompts/auth.ts @@ -0,0 +1,23 @@ +import { cancel, confirm, isCancel } from "@clack/prompts"; +import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; + +export async function getAuthChoice( + auth: boolean | undefined, + hasDatabase: boolean, +): Promise { + if (!hasDatabase) return false; + if (auth !== undefined) return auth; + + const response = await confirm({ + message: "Would you like to add authentication with Better-Auth?", + initialValue: DEFAULT_CONFIG.auth, + }); + + if (isCancel(response)) { + cancel(pc.red("Operation cancelled")); + process.exit(0); + } + + return response; +} diff --git a/apps/cli/src/prompts/config-prompts.ts b/apps/cli/src/prompts/config-prompts.ts new file mode 100644 index 0000000..3955603 --- /dev/null +++ b/apps/cli/src/prompts/config-prompts.ts @@ -0,0 +1,62 @@ +import { cancel, group } from "@clack/prompts"; +import pc from "picocolors"; +import type { + PackageManager, + ProjectConfig, + ProjectDatabase, + ProjectFeature, + ProjectORM, +} from "../types"; +import { getAuthChoice } from "./auth"; +import { getDatabaseChoice } from "./database"; +import { getFeaturesChoice } from "./features"; +import { getGitChoice } from "./git"; +import { getORMChoice } from "./orm"; +import { getPackageManagerChoice } from "./package-manager"; +import { getProjectName } from "./project-name"; + +interface PromptGroupResults { + projectName: string; + database: ProjectDatabase; + orm: ProjectORM; + auth: boolean; + features: ProjectFeature[]; + git: boolean; + packageManager: PackageManager; +} + +export async function gatherConfig( + flags: Partial, +): Promise { + const result = await group( + { + projectName: async () => { + return getProjectName(flags.projectName); + }, + database: () => getDatabaseChoice(flags.database), + orm: ({ results }) => + getORMChoice(flags.orm, results.database !== "none"), + auth: ({ results }) => + getAuthChoice(flags.auth, results.database !== "none"), + features: () => getFeaturesChoice(flags.features), + git: () => getGitChoice(flags.git), + packageManager: () => getPackageManagerChoice(flags.packageManager), + }, + { + onCancel: () => { + cancel(pc.red("Operation cancelled")); + process.exit(0); + }, + }, + ); + + return { + projectName: result.projectName, + database: result.database, + orm: result.orm, + auth: result.auth, + features: result.features, + git: result.git, + packageManager: result.packageManager, + }; +} diff --git a/apps/cli/src/prompts/database.ts b/apps/cli/src/prompts/database.ts new file mode 100644 index 0000000..e45e668 --- /dev/null +++ b/apps/cli/src/prompts/database.ts @@ -0,0 +1,38 @@ +import { cancel, isCancel, select } from "@clack/prompts"; +import pc from "picocolors"; +import type { ProjectDatabase } from "../types"; + +export async function getDatabaseChoice( + database?: ProjectDatabase, +): Promise { + if (database !== undefined) return database; + + const response = await select({ + message: "Which database would you like to use?", + options: [ + { + value: "none", + label: "None", + hint: "No database setup", + }, + { + value: "sqlite", + label: "SQLite", + hint: "by Turso (recommended)", + }, + { + value: "postgres", + label: "PostgreSQL", + hint: "Traditional relational database", + }, + ], + initialValue: "sqlite", + }); + + if (isCancel(response)) { + cancel(pc.red("Operation cancelled")); + process.exit(0); + } + + return response; +} diff --git a/apps/cli/src/prompts/features.ts b/apps/cli/src/prompts/features.ts new file mode 100644 index 0000000..8560c6e --- /dev/null +++ b/apps/cli/src/prompts/features.ts @@ -0,0 +1,38 @@ +import { cancel, isCancel, multiselect } from "@clack/prompts"; +import pc from "picocolors"; +import type { ProjectFeature } from "../types"; + +export async function getFeaturesChoice( + features?: ProjectFeature[], +): Promise { + if (features !== undefined) return features; + + const response = await multiselect({ + message: "Which features would you like to add?", + options: [ + { + value: "docker", + label: "Docker setup", + hint: "Containerize your application", + }, + { + value: "github-actions", + label: "GitHub Actions", + hint: "CI/CD workflows", + }, + { + value: "SEO", + label: "Basic SEO setup", + hint: "Search engine optimization configuration", + }, + ], + required: false, + }); + + if (isCancel(response)) { + cancel(pc.red("Operation cancelled")); + process.exit(0); + } + + return response; +} diff --git a/apps/cli/src/prompts/git.ts b/apps/cli/src/prompts/git.ts new file mode 100644 index 0000000..2fb4e8c --- /dev/null +++ b/apps/cli/src/prompts/git.ts @@ -0,0 +1,19 @@ +import { cancel, confirm, isCancel } from "@clack/prompts"; +import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; + +export async function getGitChoice(git?: boolean): Promise { + if (git !== undefined) return git; + + const response = await confirm({ + message: "Initialize a new git repository?", + initialValue: DEFAULT_CONFIG.git, + }); + + if (isCancel(response)) { + cancel(pc.red("Operation cancelled")); + process.exit(0); + } + + return response; +} diff --git a/apps/cli/src/prompts/orm.ts b/apps/cli/src/prompts/orm.ts new file mode 100644 index 0000000..fd480b4 --- /dev/null +++ b/apps/cli/src/prompts/orm.ts @@ -0,0 +1,30 @@ +import { cancel, isCancel, select } from "@clack/prompts"; +import pc from "picocolors"; +import type { ProjectORM } from "../types"; + +export async function getORMChoice( + orm: ProjectORM | undefined, + hasDatabase: boolean, +): Promise { + if (!hasDatabase) return "none"; + if (orm !== undefined) return orm; + + const response = await select({ + message: "Which ORM would you like to use?", + options: [ + { + value: "drizzle", + label: "Drizzle", + hint: "Type-safe, lightweight ORM (recommended)", + }, + ], + initialValue: "drizzle", + }); + + if (isCancel(response)) { + cancel(pc.red("Operation cancelled")); + process.exit(0); + } + + return response; +} diff --git a/apps/cli/src/prompts/package-manager.ts b/apps/cli/src/prompts/package-manager.ts new file mode 100644 index 0000000..38d8fac --- /dev/null +++ b/apps/cli/src/prompts/package-manager.ts @@ -0,0 +1,52 @@ +import { cancel, confirm, isCancel, select } from "@clack/prompts"; +import pc from "picocolors"; +import type { PackageManager } from "../types"; +import { getUserPkgManager } from "../utils/get-package-manager"; + +export async function getPackageManagerChoice( + packageManager?: PackageManager, +): Promise { + if (packageManager !== undefined) return packageManager; + + const detectedPackageManager = getUserPkgManager(); + const useDetected = await confirm({ + message: `Use ${detectedPackageManager} as your package manager?`, + }); + + if (isCancel(useDetected)) { + cancel(pc.red("Operation cancelled")); + process.exit(0); + } + + if (useDetected) return detectedPackageManager; + + const response = await select({ + message: "Which package manager would you like to use?", + options: [ + { value: "npm", label: "npm", hint: "Node Package Manager" }, + { + value: "pnpm", + label: "pnpm", + hint: "Fast, disk space efficient package manager", + }, + { + value: "yarn", + label: "yarn", + hint: "Fast, reliable, and secure dependency management", + }, + { + value: "bun", + label: "bun", + hint: "All-in-one JavaScript runtime & toolkit (recommended)", + }, + ], + initialValue: "bun", + }); + + if (isCancel(response)) { + cancel(pc.red("Operation cancelled")); + process.exit(0); + } + + return response; +} diff --git a/apps/cli/src/prompts/project-name.ts b/apps/cli/src/prompts/project-name.ts new file mode 100644 index 0000000..d4dc0d1 --- /dev/null +++ b/apps/cli/src/prompts/project-name.ts @@ -0,0 +1,95 @@ +import path from "node:path"; +import { cancel, isCancel, text } from "@clack/prompts"; +import fs from "fs-extra"; +import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; + +const INVALID_CHARS = ["<", ">", ":", '"', "|", "?", "*"]; + +const MAX_LENGTH = 255; + +function validateDirectoryName(name: string): string | undefined { + if (!name) return "Project name cannot be empty"; + if (name.length > MAX_LENGTH) { + return `Project name must be less than ${MAX_LENGTH} characters`; + } + if (INVALID_CHARS.some((char) => name.includes(char))) { + return "Project name contains invalid characters"; + } + if (name.startsWith(".") || name.startsWith("-")) { + return "Project name cannot start with a dot or dash"; + } + if ( + name.toLowerCase() === "node_modules" || + name.toLowerCase() === "favicon.ico" + ) { + return "Project name is reserved"; + } + return undefined; +} + +export async function getProjectName(initialName?: string): Promise { + if (initialName) { + const finalDirName = path.basename(initialName); + const validationError = validateDirectoryName(finalDirName); + if (!validationError) { + const projectDir = path.resolve(process.cwd(), initialName); + if ( + !fs.pathExistsSync(projectDir) || + fs.readdirSync(projectDir).length === 0 + ) { + return initialName; + } + } + } + + let isValid = false; + let projectName = ""; + let defaultName = DEFAULT_CONFIG.projectName; + let counter = 1; + + while (fs.pathExistsSync(path.resolve(process.cwd(), defaultName))) { + defaultName = `${DEFAULT_CONFIG.projectName}-${counter}`; + counter++; + } + + while (!isValid) { + const response = await text({ + message: "What is your project named? (directory name or path)", + placeholder: defaultName, + initialValue: initialName, + defaultValue: defaultName, + validate: (value) => { + const nameToUse = value.trim() || defaultName; + + const finalDirName = path.basename(nameToUse); + const validationError = validateDirectoryName(finalDirName); + if (validationError) return validationError; + + const projectDir = path.resolve(process.cwd(), nameToUse); + if (!projectDir.startsWith(process.cwd())) { + return "Project path must be within current directory"; + } + + if (fs.pathExistsSync(projectDir)) { + const dirContents = fs.readdirSync(projectDir); + if (dirContents.length > 0) { + return `Directory "${nameToUse}" already exists and is not empty. Please choose a different name.`; + } + } + + isValid = true; + return undefined; + }, + }); + + if (isCancel(response)) { + cancel(pc.red("Operation cancelled.")); + process.exit(0); + } + + projectName = response || defaultName; + } + + return projectName; +} diff --git a/apps/cli/src/types.ts b/apps/cli/src/types.ts index c1b2776..fa8a7d2 100644 --- a/apps/cli/src/types.ts +++ b/apps/cli/src/types.ts @@ -1,10 +1,10 @@ export type ProjectFeature = "docker" | "github-actions" | "SEO"; -export type ProjectDatabase = "sqlite" | "postgres"; +export type ProjectDatabase = "sqlite" | "postgres" | "none"; export type PackageManager = "npm" | "yarn" | "pnpm" | "bun"; -export type ProjectORM = "drizzle" | "prisma"; +export type ProjectORM = "drizzle" | "prisma" | "none"; export type ProjectConfig = { yes?: boolean; diff --git a/apps/cli/src/utils/display-config.ts b/apps/cli/src/utils/display-config.ts new file mode 100644 index 0000000..03f53bb --- /dev/null +++ b/apps/cli/src/utils/display-config.ts @@ -0,0 +1,32 @@ +import pc from "picocolors"; +import type { ProjectConfig } from "../types"; + +export function displayConfig(config: Partial) { + const configDisplay = []; + + if (config.projectName) { + configDisplay.push(`${pc.blue("Project Name:")} ${config.projectName}`); + } + if (config.database) { + configDisplay.push(`${pc.blue("Database:")} ${config.database}`); + } + if (config.orm) { + configDisplay.push(`${pc.blue("ORM:")} ${config.orm}`); + } + if (config.auth !== undefined) { + configDisplay.push(`${pc.blue("Authentication:")} ${config.auth}`); + } + if (config.features?.length) { + configDisplay.push(`${pc.blue("Features:")} ${config.features.join(", ")}`); + } + if (config.git !== undefined) { + configDisplay.push(`${pc.blue("Git Init:")} ${config.git}`); + } + if (config.packageManager) { + configDisplay.push( + `${pc.blue("Package Manager:")} ${config.packageManager}`, + ); + } + + return configDisplay.join("\n"); +} diff --git a/apps/cli/src/utils/generate-reproducible-command.ts b/apps/cli/src/utils/generate-reproducible-command.ts index 6e1cec1..d7bc704 100644 --- a/apps/cli/src/utils/generate-reproducible-command.ts +++ b/apps/cli/src/utils/generate-reproducible-command.ts @@ -1,4 +1,4 @@ -import { DEFAULT_CONFIG } from "../consts"; +import { DEFAULT_CONFIG } from "../constants"; import type { ProjectConfig } from "../types"; export function generateReproducibleCommand(config: ProjectConfig): string { @@ -14,11 +14,17 @@ export function generateReproducibleCommand(config: ProjectConfig): string { flags.push("-y"); } + // Handle database flag if (config.database !== DEFAULT_CONFIG.database) { - flags.push(config.database === "sqlite" ? "--sqlite" : "--postgres"); + if (config.database === "none") { + flags.push("--no-database"); + } else { + flags.push(config.database === "sqlite" ? "--sqlite" : "--postgres"); + } } - if (config.orm !== DEFAULT_CONFIG.orm) { + // Handle ORM flag only if database is not "none" + if (config.database !== "none" && config.orm !== DEFAULT_CONFIG.orm) { flags.push(config.orm === "drizzle" ? "--drizzle" : "--prisma"); } diff --git a/apps/cli/src/utils/get-version.ts b/apps/cli/src/utils/get-version.ts index 2190751..8f1a3ca 100644 --- a/apps/cli/src/utils/get-version.ts +++ b/apps/cli/src/utils/get-version.ts @@ -1,6 +1,6 @@ import path from "node:path"; import fs from "fs-extra"; -import { PKG_ROOT } from "../consts"; +import { PKG_ROOT } from "../constants"; export const getVersion = () => { const packageJsonPath = path.join(PKG_ROOT, "package.json");