| '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; |