mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
add sponsors, builder, docs command in cli
This commit is contained in:
5
.changeset/cold-cups-fetch.md
Normal file
5
.changeset/cold-cups-fetch.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"create-better-t-stack": patch
|
||||
---
|
||||
|
||||
add sponsors, builder, docs command
|
||||
@@ -34,7 +34,9 @@ import { trackProjectCreation } from "./utils/analytics";
|
||||
import { displayConfig } from "./utils/display-config";
|
||||
import { generateReproducibleCommand } from "./utils/generate-reproducible-command";
|
||||
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";
|
||||
import { getProvidedFlags, processAndValidateFlags } from "./validation";
|
||||
|
||||
const t = trpcServer.initTRPC.create();
|
||||
@@ -299,6 +301,41 @@ const router = t.router({
|
||||
};
|
||||
await createProjectHandler(combinedInput);
|
||||
}),
|
||||
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.amanv.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.amanv.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({
|
||||
|
||||
25
apps/cli/src/utils/open-url.ts
Normal file
25
apps/cli/src/utils/open-url.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { log } from "@clack/prompts";
|
||||
import { execa } from "execa";
|
||||
|
||||
export async function openUrl(url: string): Promise<void> {
|
||||
const platform = process.platform;
|
||||
let command: string;
|
||||
let args: string[] = [];
|
||||
|
||||
if (platform === "darwin") {
|
||||
command = "open";
|
||||
args = [url];
|
||||
} else if (platform === "win32") {
|
||||
command = "cmd";
|
||||
args = ["/c", "start", "", url.replace(/&/g, "^&")];
|
||||
} else {
|
||||
command = "xdg-open";
|
||||
args = [url];
|
||||
}
|
||||
|
||||
try {
|
||||
await execa(command, args, { stdio: "ignore" });
|
||||
} catch {
|
||||
log.message(`Please open ${url} in your browser.`);
|
||||
}
|
||||
}
|
||||
68
apps/cli/src/utils/sponsors.ts
Normal file
68
apps/cli/src/utils/sponsors.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { log, outro, spinner } from "@clack/prompts";
|
||||
import pc from "picocolors";
|
||||
|
||||
export interface SponsorEntry {
|
||||
readonly sponsor: {
|
||||
readonly login: string;
|
||||
readonly name?: string | null;
|
||||
readonly avatarUrl?: string | null;
|
||||
readonly websiteUrl?: string | null;
|
||||
readonly linkUrl?: string | null;
|
||||
readonly type?: string;
|
||||
};
|
||||
readonly isOneTime: boolean;
|
||||
readonly monthlyDollars?: number;
|
||||
readonly tierName?: string;
|
||||
}
|
||||
|
||||
export const SPONSORS_JSON_URL = "https://sponsors.amanv.dev/sponsors.json";
|
||||
|
||||
export async function fetchSponsors(
|
||||
url: string = SPONSORS_JSON_URL,
|
||||
): Promise<SponsorEntry[]> {
|
||||
const s = spinner();
|
||||
s.start("Fetching sponsors…");
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
s.stop(pc.red(`Failed to fetch sponsors: ${response.statusText}`));
|
||||
throw new Error(`Failed to fetch sponsors: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const sponsors = (await response.json()) as SponsorEntry[];
|
||||
s.stop("Sponsors fetched successfully!");
|
||||
return sponsors;
|
||||
}
|
||||
|
||||
export function displaySponsors(sponsors: SponsorEntry[]): void {
|
||||
if (sponsors.length === 0) {
|
||||
log.info("No sponsors found. You can be the first one! ✨");
|
||||
outro(
|
||||
pc.cyan(
|
||||
"Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor.",
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
sponsors.forEach((entry: SponsorEntry, idx: number) => {
|
||||
const sponsor = entry.sponsor;
|
||||
const displayName = sponsor.name ?? sponsor.login;
|
||||
const tier = entry.tierName ? ` (${entry.tierName})` : "";
|
||||
|
||||
log.step(`${idx + 1}. ${pc.green(displayName)}${pc.yellow(tier)}`);
|
||||
log.message(` ${pc.dim("GitHub:")} https://github.com/${sponsor.login}`);
|
||||
|
||||
const website = sponsor.websiteUrl ?? sponsor.linkUrl;
|
||||
if (website) {
|
||||
log.message(` ${pc.dim("Website:")} ${website}`);
|
||||
}
|
||||
});
|
||||
|
||||
log.message("");
|
||||
outro(
|
||||
pc.magenta(
|
||||
"Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor.",
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -158,7 +158,7 @@ export default function SponsorsSection() {
|
||||
>
|
||||
<Github className="h-4 w-4" />
|
||||
<span className="truncate">
|
||||
github.com/{entry.sponsor.login}
|
||||
{entry.sponsor.login}
|
||||
</span>
|
||||
</a>
|
||||
{(entry.sponsor.websiteUrl ||
|
||||
|
||||
Reference in New Issue
Block a user