UNPKG

askeroo

Version:

A modern CLI prompt library with flow control, history navigation, and conditional prompts

261 lines (258 loc) 9.25 kB
#!/usr/bin/env node import { ask, group, text, confirm, radio, multi, tasks, note, spinner, completedFields, } from "../src/index.js"; // Sleep helper function const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const flow = async () => { // Examples of different color syntax options: await note("[ Plugma ]{bg#883AE2} [v2.1.0]{dim}"); // Hex background color await completedFields(); const answers = await group(async () => { // Collect prompts individually const type = await radio({ label: "Choose a type:", shortLabel: "Type", options: [ { value: "plugin", label: "Plugin" }, { value: "widget", label: "Widget" }, ], }); const path = await text({ shortLabel: "Path", label: "Where should it be created?", initialValue: "./my-plugin", onSubmit: (value) => { if (value.startsWith("./")) { return value; } else { return "./" + value; } }, onValidate: async (value) => { if (!value.trim()) return "Path cannot be empty"; if (value.includes("..")) return "Path cannot contain '..'"; if (value === "." || value === "./" || value === "/") return "Please specify a folder name, not the root directory"; if (value.includes("//")) return "Invalid path: double slashes not allowed"; if (value.endsWith("/")) return "Path cannot end with a slash"; // Check if the base directory exists (parent path for nested paths) try { const fs = await import("fs"); const path = await import("path"); const resolvedPath = path.resolve(value); const parentPath = path.dirname(resolvedPath); const stats = await fs.promises.stat(parentPath); if (!stats.isDirectory()) { return "Base directory does not exist"; } } catch (error) { return "Base directory does not exist or is not accessible"; } return null; }, }); const framework = await radio({ label: "Select a framework:", shortLabel: "Framework", searchable: "filter", options: [ { value: "react", label: "React", color: "red" }, { value: "vue", label: "Vue", color: "green" }, { value: "svelte", label: "Svelte", color: "yellow" }, { value: "no-ui", label: "No UI" }, ], }); const template = await radio({ shortLabel: "Template", label: "Choose a template:", hintPosition: "inline-fixed", options: [ { value: "default", label: "Default", hint: "A basic template to get you started", }, { value: "minimal", label: "Rectangle creator", hint: "A minimal template to create rectangles", }, ], }); const addons = await multi({ shortLabel: "Addons", label: "Choose addons:", hintPosition: "inline-fixed", searchable: "filter", allowLoop: true, options: [ { value: "prettier", label: "Prettier", hint: "A formatter for JavaScript", }, { value: "vitest", label: "Vitest", hint: "A testing framework for JavaScript", }, { value: "playwright", label: "Playwright", hint: "A testing framework for JavaScript", }, { value: "shadcn", label: "shadcn", hint: "Beautifully designed components built with Radix UI", }, ], noneOption: { label: "None" }, }); // Conditionally prompt for shadcn config immediately after addons let shadcnConfig; if (addons.includes("shadcn")) { await group(async () => { shadcnConfig = await radio({ label: "Choose a style", shortLabel: "Style", options: [ { value: "default", label: "Default" }, { value: "shadcn-grape", label: "New York" }, ], meta: { depth: 1, group: "Shadcn", }, }); shadcnConfig = await radio({ label: "Choose a color", shortLabel: "Color", options: [ { value: "default", label: "Slate" }, { value: "shadcn-zinc", label: "Zinc" }, { value: "shadcn-neutral", label: "Neutral" }, { value: "shadcn-gray", label: "Gray" }, ], meta: { depth: 1, group: "Shadcn", }, }); }, { label: "Shadcn", flow: "phased", }); } const typescript = await confirm({ shortLabel: "TypeScript", label: "Use TypeScript?", initialValue: true, }); const test = await text({ label: "Test", initialValue: "Test", }); // Build the final answers object const answers = { path, type, framework, template, addons, typescript, }; return answers; }, { flow: "phased", hideOnCompletion: true }); // Example of sequential execution using the new API with completeOn setting const tasksList = [ { label: `Creating ${answers.type} from template`, action: async () => { await sleep(1000); }, }, ]; // Add the add-ons task if there are any addons selected if (answers.addons.length > 0) { tasksList.push({ label: `Integrating chosen add-ons`, action: async () => { await sleep(1000); }, concurrent: true, tasks: answers.addons.map((addon) => ({ label: `${addon}`, action: async () => { await sleep(Math.random() * 4000 + 1000); }, })), }); } const tasksResult = await tasks(tasksList, { concurrent: false, }); const pkgManager = await radio({ label: "Install dependencies?", shortLabel: "Dependencies", initialValue: "npm", options: [ { value: "skip", label: "Skip" }, { value: "npm", label: "npm" }, { value: "pnpm", label: "pnpm" }, { value: "yarn", label: "yarn" }, { value: "bun", label: "bun" }, { value: "deno", label: "deno" }, ], hideOnCompletion: true, // allowBack: false, }); if (pkgManager !== "skip") { await tasks.add([ { label: `Installing dependencies with ${pkgManager}`, action: async () => { await sleep(3000); // This will also run in background }, }, ]); } await note(`**Plugged in and ready to go!** 1. \`cd ./my-plugin\` 2. \`npm run dev\` 3. Import \`dist/manifest.json\` in Figma Check the docs out at https://plugma.dev. `); return { answers, pkgManager }; }; (async () => { try { const result = await ask(flow, { onCancel: async ({ results, cleanup }) => { const cancel = await spinner("Canceling...", { style: { color: "yellow", }, hideOnCompletion: true, }); await cancel.start(); await sleep(800); await cancel.stop("Cancelled"); // cleanup(); // Clean up UI first // console.log("Results:", results); process.exit(0); // User controls exit }, }); // console.log("\nResult:", JSON.stringify(result, null, 2)); } catch (error) { console.error("Error:", error); process.exit(1); } })(); //# sourceMappingURL=plugma.js.map