You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
3.7 KiB
154 lines
3.7 KiB
'use strict'; |
|
/** |
|
* Base prompt implementation |
|
* Should be extended by prompt types. |
|
*/ |
|
var _ = { |
|
assign: require('lodash/assign'), |
|
defaults: require('lodash/defaults'), |
|
clone: require('lodash/clone'), |
|
}; |
|
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, reject) => { |
|
this._run( |
|
(value) => resolve(value), |
|
(error) => reject(error) |
|
); |
|
}); |
|
} |
|
|
|
// 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, value: filteredValue }) |
|
), |
|
(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;
|
|
|