92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
#!/usr/bin/env bun
|
|
|
|
import { Config, type ResolvedConfig } from "../config.ts";
|
|
import pkg from "../../package.json" with { type: "json" };
|
|
import { loadConfig } from "c12";
|
|
import { defineCommand, runMain } from "citty";
|
|
import { cancel, group, intro, log, outro, confirm, text, spinner, multiselect, isCancel } from "@clack/prompts";
|
|
import { fetchCollections } from "../utils.ts";
|
|
import { generate } from "./utils.ts";
|
|
import { existsSync } from "node:fs";
|
|
|
|
async function getConfig() {
|
|
const { config } = await loadConfig({ name: "zod-pocketbase", rcFile: false, dotenv: true });
|
|
const { ZOD_POCKETBASE_ADMIN_EMAIL: adminEmail, ZOD_POCKETBASE_ADMIN_PASSWORD: adminPassword, ZOD_POCKETBASE_URL: url } = process.env;
|
|
const result = Config.safeParse({ ...config, adminEmail, adminPassword, url });
|
|
if (!result.success) {
|
|
log.error("Invalid fields in your config file.");
|
|
onCancel();
|
|
}
|
|
return result.data!;
|
|
}
|
|
|
|
function onCancel() {
|
|
cancel("Operation cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
async function selectCollections(config: ResolvedConfig) {
|
|
const credentialPrompts = {
|
|
url: () => text({ message: "What is the url of your pocketbase instance?", initialValue: config.url ?? "" }),
|
|
adminEmail: () => text({ message: "What is your admin's email?", initialValue: config.adminEmail ?? "" }),
|
|
adminPassword: () => text({ message: "What is your admin's password?", initialValue: config.adminPassword ?? "" }),
|
|
};
|
|
const credentials = await group(credentialPrompts, { onCancel });
|
|
const s = spinner();
|
|
s.start("Fetching collections...");
|
|
try {
|
|
const allCollections = await fetchCollections(credentials);
|
|
s.stop("Successfully fetched collections.");
|
|
const collectionNames = await multiselect({
|
|
message: "What collections do you want to generate schemas for?",
|
|
options: allCollections.map(({ name: value }) => ({ value })),
|
|
initialValues: allCollections.filter(({ name }) => !config.ignore.includes(name)).map(({ name }) => name),
|
|
});
|
|
if (isCancel(collectionNames)) onCancel();
|
|
return allCollections.filter(({ name }) => (collectionNames as string[]).includes(name));
|
|
} catch {
|
|
s.stop("Failed to fetch collections.Please check your credentials and try again.");
|
|
return selectCollections(config);
|
|
}
|
|
}
|
|
|
|
async function setGeneratedFilePath(config: ResolvedConfig) {
|
|
const output = await text({
|
|
message: "What is the generated file path?",
|
|
initialValue: config.output,
|
|
validate: (value) => {
|
|
if (!value) return "Please enter a path.";
|
|
if (value[0] !== ".") return "Please enter a relative path.";
|
|
return;
|
|
},
|
|
});
|
|
if (isCancel(output)) onCancel();
|
|
|
|
if (existsSync(output as string)) {
|
|
const confirmed = await confirm({ message: "The file already exists, would you like to overwrite it?" });
|
|
if (isCancel(confirmed)) onCancel();
|
|
if (!confirmed) return setGeneratedFilePath(config);
|
|
}
|
|
|
|
return output as string;
|
|
}
|
|
|
|
const main = defineCommand({
|
|
meta: { name: "zod-pocketbase", version: pkg.version, description: "Generate Zod schemas for your pocketbase instance." },
|
|
run: async () => {
|
|
intro(`ZOD POCKETBASE`);
|
|
const config = await getConfig();
|
|
const collections = await selectCollections(config);
|
|
const output = await setGeneratedFilePath(config);
|
|
|
|
const s = spinner();
|
|
s.start("Generating your schemas...");
|
|
await generate(collections, { ...config, output });
|
|
s.stop("Schemas successfully generated.");
|
|
|
|
outro("Operation completed.");
|
|
},
|
|
});
|
|
|
|
runMain(main);
|