UNPKG

@grandlinex/easy-cli

Version:

Cli lib to perform common tasks

264 lines (263 loc) 11.3 kB
import fs from 'fs'; import { confirm, editor, input, number, select } from '@inquirer/prompts'; import { ShellCommand, } from '../../class/ShellComand.js'; import InteractionArgs from '../../class/InteractionArgs.js'; import ArgUtil from '../../utils/ArgUtil.js'; import { getBoolOrUndefined, getNumberOrUndefined, getStringOrUndefined, StricktOption, } from '../../lib/types.js'; export default class InteractiveAction extends ShellCommand { constructor(props) { super({ ...props, name: 'interactive', description: 'Use the interactive mode', properties: [ { key: 'interactive', type: 'null', required: true, }, ], }); } getFileOption(prop) { const path = this.resolvePath('.'); const files = fs.readdirSync(path, { withFileTypes: true }); return files .filter((f) => { if (prop.fileOnly) { return f.isFile(); } if (prop.dirOnly) { return f.isDirectory(); } return true; }) .map((f) => ({ name: !prop.fileOnly && !prop.dirOnly ? `[${f.isFile() ? 'File' : 'Dir'}] ${f.name}` : f.name, value: f.name, disabled: false, isDir: f.isDirectory(), })) .sort((a, b) => { if (a.isDir && !b.isDir) { return -1; } if (!a.isDir && b.isDir) { return 1; } return a.name.localeCompare(b.name); }); } async runCmd(sel, args) { sel.properties .filter(({ type, required }) => type === 'null' && required) .forEach(({ key }) => { args.param.set(key, null); }); const uParam = sel.properties.filter(({ type, required }) => type !== 'null' || !required); const validation = async (cnf) => { const { promise, after, validate } = cnf; let valid = false; let data; while (!valid) { data = await promise(); if (validate && data !== undefined) { const validated = validate(data); if (validated === true) { valid = true; } else if (typeof validated === 'string') { this.error(validated); } else { this.error('Invalid input'); } } else { valid = true; } } if (after && data !== undefined) { return after(data); } return data; }; if (uParam.length > 0) { const options = {}; // eslint-disable-next-line no-unreachable-loop for (const prop of uParam) { switch (prop.type) { case 'null': options[prop.key] = await validation({ promise: async () => confirm({ message: prop.description ?? prop.key, default: await getBoolOrUndefined(prop.default, false), }), validate: prop.validate, }); break; case 'path': options[prop.key] = await validation({ promise: async () => select({ message: prop.description ?? prop.key, choices: this.getFileOption(prop), default: await getStringOrUndefined(prop.default), pageSize: this.handler.getPageSize(), }), validate: prop.validate, }); break; case 'number': if (prop.editor) { options[prop.key] = await validation({ promise: async () => editor({ message: prop.description ?? prop.key, default: await getStringOrUndefined(prop.default), }), validate: prop.validate, }); } else { options[prop.key] = await validation({ promise: async () => number({ message: prop.description ?? prop.key, default: await getNumberOrUndefined(prop.default), }), validate: (ip) => { if (prop.validate) { return prop.validate(ip); } return !prop.required || !Number.isNaN(Number(ip)); }, }); } break; case 'string': if (!prop.options) { if (prop.editor) { options[prop.key] = await validation({ promise: async () => editor({ message: prop.description ?? prop.key, default: await getStringOrUndefined(prop.default), }), validate: (ip) => { if (prop.validate) { return prop.validate(ip); } return !prop.required || (ip || '').length > 0; }, }); } else { options[prop.key] = await validation({ promise: async () => input({ message: prop.description ?? prop.key, default: await getStringOrUndefined(prop.default), prefill: prop.prefill, }), validate: (ip) => { if (prop.validate) { return prop.validate(ip); } return !prop.required || (ip || '').length > 0; }, }); } } else { options[prop.key] = await validation({ promise: async () => { const option = prop.options ? await StricktOption(prop.options, this.handler) : undefined; return select({ message: prop.description ?? prop.key, default: await getStringOrUndefined(prop.default), choices: option?.map((o) => ({ name: `${o.key} - ${o.description}`, value: o.key, disabled: false, })) ?? [], pageSize: this.handler.getPageSize(), }); }, validate: prop.validate, }); } break; default: options[prop.key] = await validation({ promise: async () => select({ message: prop.description ?? prop.key, default: await getStringOrUndefined(prop.default), choices: [], pageSize: this.handler.getPageSize(), }), validate: prop.validate, }); } } this.debug(JSON.stringify(options)); uParam.forEach(({ key, type }) => { switch (type) { case 'null': if (options[key]) { args.param.set(key, null); } break; case 'number': if (options[key] !== '') { args.param.set(key, Number(options[key])); } break; case 'string': case 'path': if (options[key] !== '') { args.param.set(key, options[key]); } break; default: } }); } ArgUtil.printDebug(this, args); ArgUtil.buildCmd(this, args); return sel.run(args); } async checkForSubCmd(cmd, args) { args.cmd.push(cmd.name); if (cmd.subCommands.length > 0) { const next = await select({ message: `Select your action: > ${args.cmd.join(' > ')}`, pageSize: this.handler.getPageSize(), choices: cmd.subCommands.map((c) => ({ name: `${c.name} - ${c.description}`, value: c.name, disabled: false, })), }); const sel = cmd.subCommands.find((c) => c.name === next); return this.checkForSubCmd(sel, args); } return this.runCmd(cmd, args); } async run() { const cmdList = this.handler.getCmds(true); const cmd = await select({ message: 'Select your action', pageSize: this.handler.getPageSize(), choices: cmdList .filter((e) => e.name !== 'interactive') .map((c) => ({ name: `${c.name} - ${c.description}`, value: c.name, disabled: false, })), }); const sel = cmdList.find((c) => c.name === cmd); const args = new InteractionArgs(); return this.checkForSubCmd(sel, args); } }