UNPKG

nope-js-browser

Version:

NoPE Runtime for the Browser. For nodejs please use nope-js-node

152 lines (151 loc) 5.16 kB
import * as inquirer from "inquirer"; inquirer.registerPrompt("search-list", require("inquirer-search-list")); inquirer.registerPrompt("fuzzypath", require("inquirer-fuzzy-path")); /** * Helper to create an interactive menu using Inquirer. * Therefore definition of the menu is required. Once, * a choice is selected -> the provided callback is called. * * Normally, in the menu, a 'back' item is added to go back * to the upper menu. Additionally the user gets an 'exit' * option to leave the app. * * @param menu The menu which should be rendered * @param options Options to control the behavior of the exit-entry etc. */ export async function createInteractiveMenu(menu, options = {}) { const defaults = { addExit: true, }; const optionsToUse = Object.assign(defaults, options); // Define a default exit entry. // This will be added to every menu if required. const exitChoice = { name: "exit", type: "item", onSelect: async () => { // If the user provided an extra // callback, which should be called // on exit ==> we will perform that. if (optionsToUse.exitCallback) { await optionsToUse.exitCallback(); } // Now we are able to exit our item. process.exit(1); }, value: "exit", }; // We add the exit menu entry if required. if (optionsToUse.addExit) { menu.push(exitChoice); } let question = menu; let last = menu; /** * Helper function to find the matching item. * @param value The value which will be used during search. * @returns */ function findInMenu(value) { for (const item of question) { if (item.value === value) { return item; } } return false; } // Custom back choice. That is quite simple. // we will just make shure we are using the // last questions we had. const backChoice = { name: "back", value: "back", async onSelect() { question = last; }, type: "item", }; // Now we are running our choices in an endless loop. // If the user selects one option => we will either: // a) adapt the menu // b) execute the callback for the choice. while (true) { const result = (await inquirer.prompt([ { type: "search-list", message: "Select the operation to perform", name: "option", choices: question, }, ])).option; // Find our desired action const entry = findInMenu(result); // Now we will either: // a) adapt the menu // b) execute the callback for the choice. if (entry) { switch (entry.type) { case "item": await entry.onSelect(); break; case "menu": last = question; question = entry.items; if (!entry.preventBack && !question.includes(backChoice)) { question.push(backChoice); } if (optionsToUse.addExit && !question.includes(exitChoice)) { question.push(exitChoice); } break; } } } } if (require.main === module) { // The following code segment shows the // usage of the function async function main() { createInteractiveMenu([ { type: "item", name: "render hello", async onSelect() { console.log("hello-1"); }, value: "hello", }, { type: "menu", name: "sub-items", items: [ { type: "item", name: "render hello in submenu", async onSelect() { console.log("hello from submenu"); }, value: "hello", }, { type: "menu", name: "sub-items", items: [ { type: "item", name: "render hello in sub-sub-menu", async onSelect() { console.log("hello from sub-sub-menu"); }, value: "hello", }, ], value: "sub-sub-menu", }, ], value: "submenu", }, ]); } main().catch(console.error); }