UNPKG

@kba-tools/genesis-tool

Version:

A forked repo from ConsenSys which helps to generate genesis block for Besu Network with xemptyBlockPeriod field.

138 lines (113 loc) 4.32 kB
import { QuestionTree, AnswerMap, QuestionOption } from "./types/questions"; import readline, { ReadLine } from "readline"; export class QuestionRenderer { private _questions: QuestionTree; private _forceDefaults: boolean; constructor(questions: QuestionTree, forceDefaults?: boolean) { this._questions = questions; this._forceDefaults = !!forceDefaults; } async render(): Promise<AnswerMap> { const answers: AnswerMap = {}; const rl = readline.createInterface(process.stdin, process.stdout); try { let question: QuestionTree | undefined = this._questions; while (question) { if (question.options) { question = await this._handleOptionsQuestion(rl, question, answers); } else if (question.transformerValidator) { question = await this._handleTransformerQuestion(rl, question, answers); } else { throw new Error(`BUG! Question '${question.name}' does not include multiple choice options or a free form input transformer.`); } } } finally { rl.close(); } return answers; } async _handleOptionsQuestion(rl: ReadLine, question: QuestionTree, answers: AnswerMap): Promise<QuestionTree | undefined> { let prompt = question.prompt + "\n"; if (!question.options) { throw new Error(`BUG! Question '${question.name}' does not include any options from which to select`); } let defaultOption: QuestionOption | null = null; for (let i = 1; i < question.options.length + 1; i++) { const option = question.options[i - 1]; if (option.default && !defaultOption) { defaultOption = option; } prompt += `\t${i}. ${option.label}\n`; } const rawInput = await this._askQuestion(rl, prompt); const choice = parseInt(rawInput, 10); if (choice >= 1 && choice <= question.options.length) { const selectedOption = question.options[choice - 1]; // we're 1 indexed on the answers here if (question.transformerValidator) { return this._handleTransformerAnswer( question, selectedOption.value, answers, selectedOption.nextQuestion ); } else { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment answers[question.name] = selectedOption.value; return selectedOption.nextQuestion; } } else if (rawInput === "" && defaultOption !== null) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment answers[question.name] = defaultOption.value; return defaultOption.nextQuestion; } else { return question; } } async _handleTransformerQuestion(rl: ReadLine, question: QuestionTree, answers: AnswerMap): Promise<QuestionTree | undefined> { const prompt = question.prompt; if (!question.transformerValidator) { throw new Error(`BUG! Question '${question.name}' does not include a transformer to handle free form entry.`); } const rawInput = await this._askQuestion(rl, prompt); return this._handleTransformerAnswer(question, rawInput.trim(), answers); } async _handleTransformerAnswer( question: QuestionTree, rawInput: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types answers: AnswerMap, fallbackQuestion?: QuestionTree ): Promise<QuestionTree | undefined> { if (!question.transformerValidator) { throw new Error(`BUG! Question '${question.name}' does not include a transformer to handle free form entry.`); } try { let nextQuestion = question.transformerValidator(rawInput, answers); if ( nextQuestion && typeof (nextQuestion as Promise<QuestionTree | undefined>).then === 'function' ) { nextQuestion = await nextQuestion; } return nextQuestion ? nextQuestion : fallbackQuestion; } catch { return question; } } _askQuestion(rl: ReadLine, prompt: string): Promise<string> { const forceDefaults = this._forceDefaults; if (!prompt.endsWith("\n")) { prompt += "\n"; } return new Promise((resolve, reject) => { try { if (forceDefaults) { resolve(""); } else { rl.question(prompt, resolve); } } catch (err) { reject(err); } }); } }