mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
replace commander with yargs
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { cancel, intro, log, outro, spinner } from "@clack/prompts";
|
||||
import { Command } from "commander";
|
||||
import pc from "picocolors";
|
||||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import { DEFAULT_CONFIG } from "./constants";
|
||||
import { createProject } from "./helpers/create-project";
|
||||
import { gatherConfig } from "./prompts/config-prompts";
|
||||
import type {
|
||||
CLIOptions,
|
||||
ProjectAddons,
|
||||
ProjectBackend,
|
||||
ProjectConfig,
|
||||
@@ -22,62 +22,134 @@ import { generateReproducibleCommand } from "./utils/generate-reproducible-comma
|
||||
import { getLatestCLIVersion } from "./utils/get-latest-cli-version";
|
||||
import { renderTitle } from "./utils/render-title";
|
||||
|
||||
type YargsArgv = {
|
||||
projectDirectory?: string;
|
||||
|
||||
yes?: boolean;
|
||||
database?: ProjectDatabase;
|
||||
orm?: ProjectOrm;
|
||||
auth?: boolean;
|
||||
frontend?: ProjectFrontend[];
|
||||
addons?: ProjectAddons[];
|
||||
examples?: ProjectExamples[];
|
||||
git?: boolean;
|
||||
packageManager?: ProjectPackageManager;
|
||||
install?: boolean;
|
||||
dbSetup?: ProjectDBSetup;
|
||||
backend?: ProjectBackend;
|
||||
runtime?: ProjectRuntime;
|
||||
|
||||
_: (string | number)[];
|
||||
$0: string;
|
||||
};
|
||||
|
||||
const exit = () => process.exit(0);
|
||||
process.on("SIGINT", exit);
|
||||
process.on("SIGTERM", exit);
|
||||
|
||||
const program = new Command();
|
||||
|
||||
async function main() {
|
||||
const startTime = Date.now();
|
||||
|
||||
program
|
||||
.name("create-better-t-stack")
|
||||
.description("Create a new Better-T Stack project")
|
||||
.version(getLatestCLIVersion())
|
||||
.argument("[project-directory]", "Project name/directory")
|
||||
.option("-y, --yes", "Use default configuration")
|
||||
.option(
|
||||
"--database <type>",
|
||||
"Database type (none, sqlite, postgres, mysql, mongodb)",
|
||||
)
|
||||
.option("--orm <type>", "ORM type (drizzle, prisma)")
|
||||
.option("--auth", "Include authentication")
|
||||
.option("--no-auth", "Exclude authentication")
|
||||
.option(
|
||||
"--frontend <types...>",
|
||||
"Frontend types (tanstack-router, react-router, tanstack-start, native, none)",
|
||||
)
|
||||
.option(
|
||||
"--addons <types...>",
|
||||
"Additional addons (pwa, tauri, starlight, biome, husky, none)",
|
||||
)
|
||||
.option("--examples <types...>", "Examples to include (todo, ai)")
|
||||
.option("--no-examples", "Skip all examples")
|
||||
.option("--git", "Initialize git repository")
|
||||
.option("--no-git", "Skip git initialization")
|
||||
.option("--package-manager <pm>", "Package manager (npm, pnpm, bun)")
|
||||
.option("--install", "Install dependencies")
|
||||
.option("--no-install", "Skip installing dependencies")
|
||||
.option(
|
||||
"--db-setup <setup>",
|
||||
"Database setup (turso, neon, prisma-postgres, mongodb-atlas, none)",
|
||||
)
|
||||
.option(
|
||||
"--backend <framework>",
|
||||
"Backend framework (hono, express, elysia)",
|
||||
)
|
||||
.option("--runtime <runtime>", "Runtime (bun, node)")
|
||||
.parse();
|
||||
|
||||
const s = spinner();
|
||||
|
||||
try {
|
||||
const argv = await yargs(hideBin(process.argv))
|
||||
.scriptName("create-better-t-stack")
|
||||
.usage(
|
||||
"$0 [project-directory] [options]",
|
||||
"Create a new Better-T Stack project",
|
||||
)
|
||||
.positional("project-directory", {
|
||||
describe: "Project name/directory",
|
||||
type: "string",
|
||||
})
|
||||
.option("yes", {
|
||||
alias: "y",
|
||||
type: "boolean",
|
||||
describe: "Use default configuration and skip prompts",
|
||||
default: false,
|
||||
})
|
||||
.option("database", {
|
||||
type: "string",
|
||||
describe: "Database type",
|
||||
choices: ["none", "sqlite", "postgres", "mysql", "mongodb"],
|
||||
})
|
||||
.option("orm", {
|
||||
type: "string",
|
||||
describe: "ORM type",
|
||||
choices: ["drizzle", "prisma", "none"],
|
||||
})
|
||||
.option("auth", {
|
||||
type: "boolean",
|
||||
describe: "Include authentication",
|
||||
})
|
||||
.option("frontend", {
|
||||
type: "array",
|
||||
string: true,
|
||||
describe: "Frontend types",
|
||||
choices: [
|
||||
"tanstack-router",
|
||||
"react-router",
|
||||
"tanstack-start",
|
||||
"native",
|
||||
"none",
|
||||
],
|
||||
})
|
||||
.option("addons", {
|
||||
type: "array",
|
||||
string: true,
|
||||
describe: "Additional addons",
|
||||
choices: ["pwa", "tauri", "starlight", "biome", "husky", "none"],
|
||||
})
|
||||
.option("examples", {
|
||||
type: "array",
|
||||
string: true,
|
||||
describe: "Examples to include",
|
||||
choices: ["todo", "ai", "none"],
|
||||
})
|
||||
.option("git", {
|
||||
type: "boolean",
|
||||
describe: "Initialize git repository",
|
||||
})
|
||||
.option("package-manager", {
|
||||
alias: "pm",
|
||||
type: "string",
|
||||
describe: "Package manager",
|
||||
choices: ["npm", "pnpm", "bun"],
|
||||
})
|
||||
.option("install", {
|
||||
type: "boolean",
|
||||
describe: "Install dependencies (use --no-install to explicitly skip)",
|
||||
})
|
||||
.option("db-setup", {
|
||||
type: "string",
|
||||
describe: "Database setup",
|
||||
choices: ["turso", "neon", "prisma-postgres", "mongodb-atlas", "none"],
|
||||
})
|
||||
.option("backend", {
|
||||
type: "string",
|
||||
describe: "Backend framework",
|
||||
choices: ["hono", "express", "elysia"],
|
||||
})
|
||||
.option("runtime", {
|
||||
type: "string",
|
||||
describe: "Runtime",
|
||||
choices: ["bun", "node"],
|
||||
})
|
||||
.completion()
|
||||
.recommendCommands()
|
||||
.version(getLatestCLIVersion())
|
||||
.alias("version", "v")
|
||||
.help()
|
||||
.alias("help", "h")
|
||||
.strict()
|
||||
.wrap(null)
|
||||
.parse();
|
||||
|
||||
renderTitle();
|
||||
intro(pc.magenta("Creating a new Better-T-Stack project"));
|
||||
|
||||
const options = program.opts() as CLIOptions;
|
||||
const projectDirectory = program.args[0];
|
||||
const options = argv as YargsArgv;
|
||||
const projectDirectory = options.projectDirectory;
|
||||
|
||||
const flagConfig = processAndValidateFlags(options, projectDirectory);
|
||||
|
||||
@@ -104,11 +176,9 @@ async function main() {
|
||||
await createProject(config);
|
||||
|
||||
log.success(
|
||||
pc.blue(
|
||||
`You can reproduce this setup with the following command:\n${pc.white(
|
||||
generateReproducibleCommand(config),
|
||||
)}`,
|
||||
),
|
||||
`You can reproduce this setup with the following command:\n${pc.white(
|
||||
generateReproducibleCommand(config),
|
||||
)}`,
|
||||
);
|
||||
|
||||
const elapsedTimeInSeconds = ((Date.now() - startTime) / 1000).toFixed(2);
|
||||
@@ -120,45 +190,42 @@ async function main() {
|
||||
} catch (error) {
|
||||
s.stop(pc.red("Failed"));
|
||||
if (error instanceof Error) {
|
||||
cancel(pc.red(`An unexpected error occurred: ${error.message}`));
|
||||
if (error.name === "YError") {
|
||||
cancel(pc.red(`Invalid arguments: ${error.message}`));
|
||||
} else {
|
||||
cancel(pc.red(`An unexpected error occurred: ${error.message}`));
|
||||
}
|
||||
process.exit(1);
|
||||
} else {
|
||||
cancel(pc.red("An unexpected error occurred."));
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processAndValidateFlags(
|
||||
options: CLIOptions,
|
||||
options: YargsArgv,
|
||||
projectDirectory?: string,
|
||||
): Partial<ProjectConfig> {
|
||||
const config: Partial<ProjectConfig> = {};
|
||||
|
||||
if (options.database) {
|
||||
if (
|
||||
!["none", "sqlite", "postgres", "mysql", "mongodb"].includes(
|
||||
options.database,
|
||||
)
|
||||
) {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Invalid database type: ${options.database}. Must be none, sqlite, postgres, mysql, or mongodb.`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
config.database = options.database as ProjectDatabase;
|
||||
}
|
||||
|
||||
if (options.orm) {
|
||||
if (!["drizzle", "prisma"].includes(options.orm)) {
|
||||
cancel(
|
||||
pc.red(`Invalid ORM type: ${options.orm}. Must be drizzle or prisma.`),
|
||||
);
|
||||
process.exit(1);
|
||||
if (options.orm === "none") {
|
||||
config.orm = "none";
|
||||
} else {
|
||||
config.orm = options.orm as ProjectOrm;
|
||||
}
|
||||
config.orm = options.orm as ProjectOrm;
|
||||
}
|
||||
|
||||
if (config.database === "mongodb" && config.orm === "drizzle") {
|
||||
if (
|
||||
(config.database ?? options.database) === "mongodb" &&
|
||||
(config.orm ?? options.orm) === "drizzle"
|
||||
) {
|
||||
cancel(
|
||||
pc.red(
|
||||
"MongoDB is only available with Prisma. Cannot use --database mongodb with --orm drizzle",
|
||||
@@ -168,23 +235,12 @@ function processAndValidateFlags(
|
||||
}
|
||||
|
||||
if (options.dbSetup) {
|
||||
if (
|
||||
!["turso", "prisma-postgres", "mongodb-atlas", "neon", "none"].includes(
|
||||
options.dbSetup,
|
||||
)
|
||||
) {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Invalid database setup: ${options.dbSetup}. Must be turso, prisma-postgres, mongodb-atlas, neon, or none.`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
const dbSetup = options.dbSetup as ProjectDBSetup | "none";
|
||||
|
||||
if (options.dbSetup !== "none") {
|
||||
config.dbSetup = options.dbSetup as ProjectDBSetup;
|
||||
if (dbSetup !== "none") {
|
||||
config.dbSetup = dbSetup;
|
||||
|
||||
if (options.dbSetup === "turso") {
|
||||
if (dbSetup === "turso") {
|
||||
if (options.database && options.database !== "sqlite") {
|
||||
cancel(
|
||||
pc.red(
|
||||
@@ -204,7 +260,7 @@ function processAndValidateFlags(
|
||||
process.exit(1);
|
||||
}
|
||||
config.orm = "drizzle";
|
||||
} else if (options.dbSetup === "prisma-postgres") {
|
||||
} else if (dbSetup === "prisma-postgres") {
|
||||
if (options.database && options.database !== "postgres") {
|
||||
cancel(
|
||||
pc.red(
|
||||
@@ -215,7 +271,7 @@ function processAndValidateFlags(
|
||||
}
|
||||
config.database = "postgres";
|
||||
|
||||
if (options.orm && options.orm !== "prisma") {
|
||||
if (options.orm && options.orm !== "prisma" && options.orm !== "none") {
|
||||
cancel(
|
||||
pc.red(
|
||||
"Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with a different ORM.",
|
||||
@@ -224,7 +280,7 @@ function processAndValidateFlags(
|
||||
process.exit(1);
|
||||
}
|
||||
config.orm = "prisma";
|
||||
} else if (options.dbSetup === "mongodb-atlas") {
|
||||
} else if (dbSetup === "mongodb-atlas") {
|
||||
if (options.database && options.database !== "mongodb") {
|
||||
cancel(
|
||||
pc.red(
|
||||
@@ -235,7 +291,7 @@ function processAndValidateFlags(
|
||||
}
|
||||
config.database = "mongodb";
|
||||
config.orm = "prisma";
|
||||
} else if (options.dbSetup === "neon") {
|
||||
} else if (dbSetup === "neon") {
|
||||
if (options.database && options.database !== "postgres") {
|
||||
cancel(
|
||||
pc.red(
|
||||
@@ -251,7 +307,8 @@ function processAndValidateFlags(
|
||||
}
|
||||
}
|
||||
|
||||
if (config.database === "none") {
|
||||
const effectiveDatabase = config.database ?? options.database;
|
||||
if (effectiveDatabase === "none") {
|
||||
if (options.auth === true) {
|
||||
cancel(
|
||||
pc.red(
|
||||
@@ -261,72 +318,42 @@ function processAndValidateFlags(
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (options.orm && options.orm !== "none") {
|
||||
const effectiveOrm = config.orm ?? options.orm;
|
||||
if (effectiveOrm && effectiveOrm !== "none") {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Cannot use ORM with no database. Cannot use --orm ${options.orm} with --database none.`,
|
||||
`Cannot use ORM with no database. Cannot use --orm ${effectiveOrm} with --database none.`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
config.orm = "none";
|
||||
|
||||
if (options.dbSetup && options.dbSetup !== "none") {
|
||||
const effectiveDbSetup = config.dbSetup ?? options.dbSetup;
|
||||
if (effectiveDbSetup && effectiveDbSetup !== "none") {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Database setup requires a database. Cannot use --db-setup ${options.dbSetup} with --database none.`,
|
||||
`Database setup requires a database. Cannot use --db-setup ${effectiveDbSetup} with --database none.`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
config.dbSetup = "none";
|
||||
}
|
||||
|
||||
if ("auth" in options) {
|
||||
if (options.auth !== undefined) {
|
||||
config.auth = options.auth;
|
||||
}
|
||||
|
||||
if (options.backend) {
|
||||
if (!["hono", "elysia", "express"].includes(options.backend)) {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Invalid backend framework: ${options.backend}. Must be hono, elysia, or express.`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
config.backend = options.backend as ProjectBackend;
|
||||
}
|
||||
|
||||
if (options.runtime) {
|
||||
if (!["bun", "node"].includes(options.runtime)) {
|
||||
cancel(
|
||||
pc.red(`Invalid runtime: ${options.runtime}. Must be bun or node.`),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
config.runtime = options.runtime as ProjectRuntime;
|
||||
}
|
||||
|
||||
if (options.frontend && options.frontend.length > 0) {
|
||||
const validFrontends = [
|
||||
"tanstack-router",
|
||||
"react-router",
|
||||
"tanstack-start",
|
||||
"native",
|
||||
"none",
|
||||
];
|
||||
const invalidFrontends = options.frontend.filter(
|
||||
(frontend: string) => !validFrontends.includes(frontend),
|
||||
);
|
||||
|
||||
if (invalidFrontends.length > 0) {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Invalid frontend(s): ${invalidFrontends.join(", ")}. Valid options are: ${validFrontends.join(", ")}.`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (options.frontend.includes("none")) {
|
||||
if (options.frontend.length > 1) {
|
||||
cancel(pc.red(`Cannot combine 'none' with other frontend options.`));
|
||||
@@ -335,11 +362,7 @@ function processAndValidateFlags(
|
||||
config.frontend = [];
|
||||
} else {
|
||||
const validOptions = options.frontend.filter(
|
||||
(f): f is ProjectFrontend =>
|
||||
f === "tanstack-router" ||
|
||||
f === "react-router" ||
|
||||
f === "tanstack-start" ||
|
||||
f === "native",
|
||||
(f): f is ProjectFrontend => f !== "none",
|
||||
);
|
||||
|
||||
const webFrontends = validOptions.filter(
|
||||
@@ -357,26 +380,11 @@ function processAndValidateFlags(
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
config.frontend = validOptions;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.addons && options.addons.length > 0) {
|
||||
const validAddons = ["pwa", "tauri", "biome", "husky", "starlight", "none"];
|
||||
const invalidAddons = options.addons.filter(
|
||||
(addon: string) => !validAddons.includes(addon),
|
||||
);
|
||||
|
||||
if (invalidAddons.length > 0) {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Invalid addon(s): ${invalidAddons.join(", ")}. Valid options are: ${validAddons.join(", ")}.`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (options.addons.includes("none")) {
|
||||
if (options.addons.length > 1) {
|
||||
cancel(pc.red(`Cannot combine 'none' with other addons.`));
|
||||
@@ -385,12 +393,7 @@ function processAndValidateFlags(
|
||||
config.addons = [];
|
||||
} else {
|
||||
const validOptions = options.addons.filter(
|
||||
(addon): addon is ProjectAddons =>
|
||||
addon === "pwa" ||
|
||||
addon === "tauri" ||
|
||||
addon === "biome" ||
|
||||
addon === "husky" ||
|
||||
addon === "starlight",
|
||||
(addon): addon is ProjectAddons => addon !== "none",
|
||||
);
|
||||
|
||||
const webSpecificAddons = ["pwa", "tauri"];
|
||||
@@ -398,57 +401,56 @@ function processAndValidateFlags(
|
||||
webSpecificAddons.includes(addon),
|
||||
);
|
||||
|
||||
const hasCompatibleWebFrontend = config.frontend?.some(
|
||||
const effectiveFrontend =
|
||||
config.frontend ?? (options.yes ? DEFAULT_CONFIG.frontend : undefined);
|
||||
|
||||
const hasCompatibleWebFrontend = effectiveFrontend?.some(
|
||||
(f) => f === "tanstack-router" || f === "react-router",
|
||||
);
|
||||
|
||||
if (
|
||||
hasWebSpecificAddons &&
|
||||
!hasCompatibleWebFrontend &&
|
||||
!(
|
||||
options.yes &&
|
||||
DEFAULT_CONFIG.frontend.some(
|
||||
(f) => f === "tanstack-router" || f === "react-router",
|
||||
)
|
||||
)
|
||||
) {
|
||||
cancel(
|
||||
pc.red(
|
||||
"PWA and Tauri addons require tanstack-router or react-router. Cannot use these addons with your frontend selection.",
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
if (hasWebSpecificAddons && !hasCompatibleWebFrontend) {
|
||||
if (options.frontend) {
|
||||
cancel(
|
||||
pc.red(
|
||||
"PWA and Tauri addons require tanstack-router or react-router. Cannot use these addons with your frontend selection.",
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
} else if (!options.yes) {
|
||||
} else {
|
||||
cancel(
|
||||
pc.red(
|
||||
"PWA and Tauri addons require tanstack-router or react-router (default frontend incompatible).",
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (validOptions.includes("husky") && !validOptions.includes("biome")) {
|
||||
validOptions.push("biome");
|
||||
}
|
||||
|
||||
config.addons = validOptions;
|
||||
config.addons = [...new Set(validOptions)];
|
||||
}
|
||||
}
|
||||
|
||||
if ("examples" in options) {
|
||||
if (options.examples === false) {
|
||||
config.examples = [];
|
||||
} else if (Array.isArray(options.examples)) {
|
||||
const validExamples = ["todo", "ai"];
|
||||
const invalidExamples = options.examples.filter(
|
||||
(example: string) => !validExamples.includes(example),
|
||||
);
|
||||
|
||||
if (invalidExamples.length > 0) {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Invalid example(s): ${invalidExamples.join(", ")}. Valid options are: ${validExamples.join(", ")}.`,
|
||||
),
|
||||
);
|
||||
if (options.examples && options.examples.length > 0) {
|
||||
if (options.examples.includes("none")) {
|
||||
if (options.examples.length > 1) {
|
||||
cancel(pc.red("Cannot combine 'none' with other examples."));
|
||||
process.exit(1);
|
||||
}
|
||||
config.examples = [];
|
||||
} else {
|
||||
const validExamples = options.examples.filter(
|
||||
(ex): ex is ProjectExamples => ex !== "none",
|
||||
);
|
||||
|
||||
const effectiveBackend = config.backend ?? options.backend;
|
||||
if (
|
||||
options.examples.includes("ai") &&
|
||||
(options.backend === "elysia" || config.backend === "elysia") &&
|
||||
validExamples.includes("ai") &&
|
||||
effectiveBackend === "elysia" &&
|
||||
!(options.yes && DEFAULT_CONFIG.backend !== "elysia")
|
||||
) {
|
||||
cancel(
|
||||
@@ -459,54 +461,47 @@ function processAndValidateFlags(
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const hasWebFrontend = config.frontend?.some((f) =>
|
||||
const effectiveFrontend =
|
||||
config.frontend ??
|
||||
(options.frontend?.filter((f) => f !== "none") as ProjectFrontend[]) ??
|
||||
(options.yes ? DEFAULT_CONFIG.frontend : undefined);
|
||||
|
||||
const hasWebFrontend = effectiveFrontend?.some((f) =>
|
||||
["tanstack-router", "react-router", "tanstack-start"].includes(f),
|
||||
);
|
||||
|
||||
if (
|
||||
options.examples.length > 0 &&
|
||||
!hasWebFrontend &&
|
||||
(!options.frontend ||
|
||||
!options.frontend.some((f) =>
|
||||
["tanstack-router", "react-router", "tanstack-start"].includes(f),
|
||||
)) &&
|
||||
!(
|
||||
options.yes &&
|
||||
DEFAULT_CONFIG.frontend.some((f) =>
|
||||
["tanstack-router", "react-router", "tanstack-start"].includes(f),
|
||||
)
|
||||
)
|
||||
) {
|
||||
cancel(
|
||||
pc.red(
|
||||
"Examples require a web frontend (tanstack-router, react-router, or tanstack-start). Cannot use --examples without a compatible frontend.",
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
if (!hasWebFrontend) {
|
||||
if (options.frontend) {
|
||||
cancel(
|
||||
pc.red(
|
||||
"Examples require a web frontend (tanstack-router, react-router, or tanstack-start). Cannot use --examples with your frontend selection.",
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
} else if (!options.yes) {
|
||||
} else {
|
||||
cancel(
|
||||
pc.red(
|
||||
"Examples require a web frontend (tanstack-router, react-router, or tanstack-start) (default frontend incompatible).",
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
config.examples = options.examples.filter(
|
||||
(ex): ex is ProjectExamples => ex === "todo" || ex === "ai",
|
||||
);
|
||||
|
||||
config.examples = validExamples;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.packageManager) {
|
||||
if (!["npm", "pnpm", "bun"].includes(options.packageManager)) {
|
||||
cancel(
|
||||
pc.red(
|
||||
`Invalid package manager: ${options.packageManager}. Must be npm, pnpm, or bun.`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
config.packageManager = options.packageManager as ProjectPackageManager;
|
||||
}
|
||||
|
||||
if ("git" in options) {
|
||||
if (options.git !== undefined) {
|
||||
config.git = options.git;
|
||||
}
|
||||
|
||||
if ("install" in options) {
|
||||
if (options.install !== undefined) {
|
||||
config.noInstall = !options.install;
|
||||
}
|
||||
|
||||
@@ -518,14 +513,15 @@ function processAndValidateFlags(
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
log.error("Aborting installation...");
|
||||
log.error("Aborting installation due to unexpected error...");
|
||||
if (err instanceof Error) {
|
||||
log.error(err.message);
|
||||
console.error(err.stack);
|
||||
} else {
|
||||
log.error(
|
||||
"An unknown error has occurred. Please open an issue on GitHub with the below:",
|
||||
);
|
||||
console.log(err);
|
||||
console.error(err);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -6,15 +6,22 @@ export type ProjectDatabase =
|
||||
| "none";
|
||||
export type ProjectOrm = "drizzle" | "prisma" | "none";
|
||||
export type ProjectPackageManager = "npm" | "pnpm" | "bun";
|
||||
export type ProjectAddons = "pwa" | "biome" | "tauri" | "husky" | "starlight";
|
||||
export type ProjectAddons =
|
||||
| "pwa"
|
||||
| "biome"
|
||||
| "tauri"
|
||||
| "husky"
|
||||
| "starlight"
|
||||
| "none";
|
||||
export type ProjectBackend = "hono" | "elysia" | "express";
|
||||
export type ProjectRuntime = "node" | "bun";
|
||||
export type ProjectExamples = "todo" | "ai";
|
||||
export type ProjectExamples = "todo" | "ai" | "none";
|
||||
export type ProjectFrontend =
|
||||
| "react-router"
|
||||
| "tanstack-router"
|
||||
| "tanstack-start"
|
||||
| "native";
|
||||
| "native"
|
||||
| "none";
|
||||
export type ProjectDBSetup =
|
||||
| "turso"
|
||||
| "prisma-postgres"
|
||||
|
||||
@@ -42,7 +42,7 @@ export function generateReproducibleCommand(config: ProjectConfig): string {
|
||||
if (config.examples && config.examples.length > 0) {
|
||||
flags.push(`--examples ${config.examples.join(" ")}`);
|
||||
} else {
|
||||
flags.push("--no-examples");
|
||||
flags.push("--examples none");
|
||||
}
|
||||
|
||||
if (config.packageManager) {
|
||||
|
||||
Reference in New Issue
Block a user