Files
create-better-t-stack/apps/cli/src/index.ts
2025-06-22 22:49:44 +05:30

138 lines
3.5 KiB
TypeScript

import { intro, log } from "@clack/prompts";
import { consola } from "consola";
import pc from "picocolors";
import { createCli, trpcServer, zod as z } from "trpc-cli";
import {
addAddonsHandler,
createProjectHandler,
} from "./helpers/project-generation/command-handlers";
import {
AddonsSchema,
APISchema,
BackendSchema,
DatabaseSchema,
DatabaseSetupSchema,
ExamplesSchema,
FrontendSchema,
ORMSchema,
PackageManagerSchema,
ProjectNameSchema,
RuntimeSchema,
} from "./types";
import { getLatestCLIVersion } from "./utils/get-latest-cli-version";
import { openUrl } from "./utils/open-url";
import { renderTitle } from "./utils/render-title";
import { displaySponsors, fetchSponsors } from "./utils/sponsors";
const t = trpcServer.initTRPC.create();
const router = t.router({
init: t.procedure
.meta({
description: "Create a new Better-T Stack project",
default: true,
})
.input(
z.tuple([
ProjectNameSchema.optional(),
z
.object({
yes: z
.boolean()
.optional()
.default(false)
.describe("Use default configuration"),
database: DatabaseSchema.optional(),
orm: ORMSchema.optional(),
auth: z.boolean().optional(),
frontend: z.array(FrontendSchema).optional(),
addons: z.array(AddonsSchema).optional(),
examples: z.array(ExamplesSchema).optional(),
git: z.boolean().optional(),
packageManager: PackageManagerSchema.optional(),
install: z.boolean().optional(),
dbSetup: DatabaseSetupSchema.optional(),
backend: BackendSchema.optional(),
runtime: RuntimeSchema.optional(),
api: APISchema.optional(),
})
.optional()
.default({}),
]),
)
.mutation(async ({ input }) => {
const [projectName, options] = input;
const combinedInput = {
projectName,
...options,
};
await createProjectHandler(combinedInput);
}),
add: t.procedure
.meta({
description: "Add addons to an existing Better-T Stack project",
})
.input(
z.tuple([
z
.object({
addons: z.array(AddonsSchema).optional().default([]),
projectDir: z.string().optional(),
install: z
.boolean()
.optional()
.default(false)
.describe("Install dependencies after adding addons"),
packageManager: PackageManagerSchema.optional(),
})
.optional()
.default({}),
]),
)
.mutation(async ({ input }) => {
const [options] = input;
await addAddonsHandler(options);
}),
sponsors: t.procedure
.meta({ description: "Show Better-T Stack sponsors" })
.mutation(async () => {
try {
renderTitle();
intro(pc.magenta("Better-T Stack Sponsors"));
const sponsors = await fetchSponsors();
displaySponsors(sponsors);
} catch (error) {
consola.error(error);
process.exit(1);
}
}),
docs: t.procedure
.meta({ description: "Open Better-T Stack documentation" })
.mutation(async () => {
const DOCS_URL = "https://better-t-stack.dev/docs";
try {
await openUrl(DOCS_URL);
log.success(pc.blue("Opened docs in your default browser."));
} catch {
log.message(`Please visit ${DOCS_URL}`);
}
}),
builder: t.procedure
.meta({ description: "Open the web-based stack builder" })
.mutation(async () => {
const BUILDER_URL = "https://better-t-stack.dev/new";
try {
await openUrl(BUILDER_URL);
log.success(pc.blue("Opened builder in your default browser."));
} catch {
log.message(`Please visit ${BUILDER_URL}`);
}
}),
});
createCli({
router,
name: "create-better-t-stack",
version: getLatestCLIVersion(),
}).run();