| 'use strict'; | |
| /** | |
| * Base prompt implementation | |
| * Should be extended by prompt types. | |
| */ | |
| var _ = require('lodash'); | |
| var chalk = require('chalk'); | |
| var runAsync = require('run-async'); | |
| var { filter, flatMap, share, take, takeUntil } = require('rxjs/operators'); | |
| var Choices = require('../objects/choices'); | |
| var ScreenManager = require('../utils/screen-manager'); | |
| class Prompt { | |
| constructor(question, rl, answers) { | |
| // Setup instance defaults property | |
| _.assign(this, { | |
| answers: answers, | |
| status: 'pending' | |
| }); | |
| // Set defaults prompt options | |
| this.opt = _.defaults(_.clone(question), { | |
| validate: () => true, | |
| filter: val => val, | |
| when: () => true, | |
| suffix: '', | |
| prefix: chalk.green('?') | |
| }); | |
| // Make sure name is present | |
| if (!this.opt.name) { | |
| this.throwParamError('name'); | |
| } | |
| // Set default message if no message defined | |
| if (!this.opt.message) { | |
| this.opt.message = this.opt.name + ':'; | |
| } | |
| // Normalize choices | |
| if (Array.isArray(this.opt.choices)) { | |
| this.opt.choices = new Choices(this.opt.choices, answers); | |
| } | |
| this.rl = rl; | |
| this.screen = new ScreenManager(this.rl); | |
| } | |
| /** | |
| * Start the Inquiry session and manage output value filtering | |
| * @return {Promise} | |
| */ | |
| run() { | |
| return new Promise(resolve => { | |
| this._run(value => resolve(value)); | |
| }); | |
| } | |
| // Default noop (this one should be overwritten in prompts) | |
| _run(cb) { | |
| cb(); | |
| } | |
| /** | |
| * Throw an error telling a required parameter is missing | |
| * @param {String} name Name of the missing param | |
| * @return {Throw Error} | |
| */ | |
| throwParamError(name) { | |
| throw new Error('You must provide a `' + name + '` parameter'); | |
| } | |
| /** | |
| * Called when the UI closes. Override to do any specific cleanup necessary | |
| */ | |
| close() { | |
| this.screen.releaseCursor(); | |
| } | |
| /** | |
| * Run the provided validation method each time a submit event occur. | |
| * @param {Rx.Observable} submit - submit event flow | |
| * @return {Object} Object containing two observables: `success` and `error` | |
| */ | |
| handleSubmitEvents(submit) { | |
| var self = this; | |
| var validate = runAsync(this.opt.validate); | |
| var asyncFilter = runAsync(this.opt.filter); | |
| var validation = submit.pipe( | |
| flatMap(value => | |
| asyncFilter(value, self.answers).then( | |
| filteredValue => | |
| validate(filteredValue, self.answers).then( | |
| isValid => ({ isValid: isValid, value: filteredValue }), | |
| err => ({ isValid: err }) | |
| ), | |
| err => ({ isValid: err }) | |
| ) | |
| ), | |
| share() | |
| ); | |
| var success = validation.pipe( | |
| filter(state => state.isValid === true), | |
| take(1) | |
| ); | |
| var error = validation.pipe( | |
| filter(state => state.isValid !== true), | |
| takeUntil(success) | |
| ); | |
| return { | |
| success: success, | |
| error: error | |
| }; | |
| } | |
| /** | |
| * Generate the prompt question string | |
| * @return {String} prompt question string | |
| */ | |
| getQuestion() { | |
| var message = | |
| this.opt.prefix + | |
| ' ' + | |
| chalk.bold(this.opt.message) + | |
| this.opt.suffix + | |
| chalk.reset(' '); | |
| // Append the default if available, and if question isn't answered | |
| if (this.opt.default != null && this.status !== 'answered') { | |
| // If default password is supplied, hide it | |
| if (this.opt.type === 'password') { | |
| message += chalk.italic.dim('[hidden] '); | |
| } else { | |
| message += chalk.dim('(' + this.opt.default + ') '); | |
| } | |
| } | |
| return message; | |
| } | |
| } | |
| module.exports = Prompt; |