symfony-style-console
Version:
Use the style and utilities of the Symfony Console in Node.js
198 lines (197 loc) • 7.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var Helper_1 = require("../Helper/Helper");
var readline;
/**
* Provides simple Q&A with text input, password input, choice and confirmation
*
* @author Florian Reuschel <florian@loilo.de>
*/
var Questionnaire = /** @class */ (function () {
/**
* Creates a new Questionnaire instance.
*
* @param output The output to use for the questions.
*/
function Questionnaire(output) {
Questionnaire.initReadline();
this.output = output;
}
/**
* Initializes the Node.js `readline` module.
*/
Questionnaire.initReadline = function () {
if (!readline) {
readline = require('readline');
}
};
/**
* Checks if a string does contain anything but whitespace.
*
* @param value The string to check
*/
Questionnaire.isFilled = function (value) {
return !!value.trim().length;
};
/**
* Disables the printing of `stdin` data to `stdout`.
*
* @param char The character that's currently going into `stdin`
*/
Questionnaire.suppressStdinOutput = function (char) {
switch (String(char)) {
case '\n':
case '\r':
case '\u0004':
process.stdin.pause();
process.stdin.removeListener('data', Questionnaire.suppressStdinOutput);
break;
default:
setImmediate(function () {
process.stdout.write("\u001B[2K\u001B[200D > ");
});
break;
}
};
/**
* Performs the actual interaction between user and terminal via `readline`.
*
* @param _ Question options
*/
Questionnaire.prototype.doAsk = function (_a) {
var _this = this;
var question = _a.question, _b = _a.validator, validator = _b === void 0 ? null : _b, _c = _a.hideInput, hideInput = _c === void 0 ? false : _c, _d = _a.errorMsg, errorMsg = _d === void 0 ? 'Invalid value.' : _d;
this.output.writeln(question);
if (hideInput) {
process.stdin.resume();
process.stdin.on('data', Questionnaire.suppressStdinOutput);
}
var rl = readline.createInterface(process.stdin, process.stdout);
rl.setPrompt(' > ');
rl.prompt();
return new Promise(function (resolve) {
rl.on('line', function (value) {
rl.history.shift();
rl.close();
_this.output.newLine();
if (!validator || validator(value)) {
resolve(value);
}
else {
_this.output.error(typeof errorMsg === 'function' ? errorMsg(value) : errorMsg);
resolve(_this.doAsk({ question: question, hideInput: hideInput, validator: validator, errorMsg: errorMsg }));
}
});
});
};
/**
* Ask for a string answer.
*
* @param question The (formatted) question to put
* @param defaultValue A default value to provide
* @param validator A validator callback
*/
Questionnaire.prototype.ask = function (question, defaultValue, validator) {
if (defaultValue === void 0) { defaultValue = null; }
if (validator === void 0) { validator = null; }
var hasDefaultValue = defaultValue != null;
var formattedQuestion = " <fg=green>" + question + "</>";
if (hasDefaultValue) {
formattedQuestion += " [<fg=yellow>" + defaultValue + "</>]";
}
formattedQuestion += ':';
return (this.doAsk({
question: formattedQuestion,
validator: validator || (hasDefaultValue ? null : Questionnaire.isFilled),
errorMsg: 'A value is required.'
})
// Return default value if exists and input is empty
.then(function (value) {
return !hasDefaultValue || Questionnaire.isFilled(value)
? value
: defaultValue;
}));
};
/**
* Ask for a string answer, hide input chars.
*
* @param question The (formatted) question to put
* @param validator A validator callback
*/
Questionnaire.prototype.askHidden = function (question, validator) {
if (validator === void 0) { validator = null; }
var formattedQuestion = " <fg=green>" + question + "</>:";
return this.doAsk({
question: formattedQuestion,
hideInput: true,
validator: validator || Questionnaire.isFilled,
errorMsg: 'A value is required.'
});
};
/**
* Ask for picking an option.
*
* @param question The (formatted) question to put
* @param choices A value-label map of options
* @param defaultValue A default value to provide
*/
Questionnaire.prototype.choice = function (question, choices, defaultValue) {
if (defaultValue === void 0) { defaultValue = null; }
var hasDefaultValue = defaultValue != null;
var flippedChoices = Helper_1.flipObject(choices);
var choiceValues = Object.keys(choices);
var choiceLabels = Object.keys(flippedChoices);
if (hasDefaultValue && !Helper_1.arrContains(choiceLabels, defaultValue)) {
throw new RangeError("Invalid default value \"" + defaultValue + "\"," +
("must be one of: " + choiceLabels
.map(function (label) { return "\"" + label + "\""; })
.join(', ')));
}
var formattedQuestion = " <fg=green>" + question + "</>";
if (hasDefaultValue) {
formattedQuestion += " [<fg=yellow>" + defaultValue + "</>]";
}
formattedQuestion += ':\n';
for (var option in choices) {
formattedQuestion += " [<fg=yellow>" + option + "</>] " + choices[option] + "\n";
}
formattedQuestion = formattedQuestion.slice(0, -1);
return (this.doAsk({
question: formattedQuestion,
validator: function (value) {
return Questionnaire.isFilled(value)
? Helper_1.arrContains(choiceValues, value)
: hasDefaultValue;
},
errorMsg: function (value) {
return "Value \"" + value + "\" is invalid.";
}
})
// Return default value if exists and input is empty
.then(function (value) {
return !hasDefaultValue || Questionnaire.isFilled(value)
? value
: flippedChoices[defaultValue];
}));
};
/**
* Ask a yes/no question.
*
* @param question The (formatted) question to put
* @param defaultValue If the answer should default to "yes"
*/
Questionnaire.prototype.confirm = function (question, defaultValue) {
if (defaultValue === void 0) { defaultValue = true; }
var formattedQuestion = " <fg=green>" + question + " (yes/no)</> [<fg=yellow>" + (defaultValue ? 'yes' : 'no') + "</>]";
var truthyRegex = /^y/i;
return this.doAsk({
question: formattedQuestion
}).then(function (value) {
return Questionnaire.isFilled(value)
? truthyRegex.test(value.trim())
: defaultValue;
});
};
return Questionnaire;
}());
exports.default = Questionnaire;