base.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. 'use strict';
  2. /**
  3. * Base prompt implementation
  4. * Should be extended by prompt types.
  5. */
  6. var _ = require('lodash');
  7. var chalk = require('chalk');
  8. var runAsync = require('run-async');
  9. var Choices = require('../objects/choices');
  10. var ScreenManager = require('../utils/screen-manager');
  11. class Prompt {
  12. constructor(question, rl, answers) {
  13. // Setup instance defaults property
  14. _.assign(this, {
  15. answers: answers,
  16. status: 'pending'
  17. });
  18. // Set defaults prompt options
  19. this.opt = _.defaults(_.clone(question), {
  20. validate: () => true,
  21. filter: val => val,
  22. when: () => true,
  23. suffix: '',
  24. prefix: chalk.green('?')
  25. });
  26. // Check to make sure prompt requirements are there
  27. if (!this.opt.message) {
  28. this.throwParamError('message');
  29. }
  30. if (!this.opt.name) {
  31. this.throwParamError('name');
  32. }
  33. // Normalize choices
  34. if (Array.isArray(this.opt.choices)) {
  35. this.opt.choices = new Choices(this.opt.choices, answers);
  36. }
  37. this.rl = rl;
  38. this.screen = new ScreenManager(this.rl);
  39. }
  40. /**
  41. * Start the Inquiry session and manage output value filtering
  42. * @return {Promise}
  43. */
  44. run() {
  45. return new Promise(resolve => {
  46. this._run(value => resolve(value));
  47. });
  48. }
  49. // Default noop (this one should be overwritten in prompts)
  50. _run(cb) {
  51. cb();
  52. }
  53. /**
  54. * Throw an error telling a required parameter is missing
  55. * @param {String} name Name of the missing param
  56. * @return {Throw Error}
  57. */
  58. throwParamError(name) {
  59. throw new Error('You must provide a `' + name + '` parameter');
  60. }
  61. /**
  62. * Called when the UI closes. Override to do any specific cleanup necessary
  63. */
  64. close() {
  65. this.screen.releaseCursor();
  66. }
  67. /**
  68. * Run the provided validation method each time a submit event occur.
  69. * @param {Rx.Observable} submit - submit event flow
  70. * @return {Object} Object containing two observables: `success` and `error`
  71. */
  72. handleSubmitEvents(submit) {
  73. var self = this;
  74. var validate = runAsync(this.opt.validate);
  75. var filter = runAsync(this.opt.filter);
  76. var validation = submit
  77. .flatMap(value =>
  78. filter(value, self.answers).then(
  79. filteredValue =>
  80. validate(filteredValue, self.answers).then(
  81. isValid => ({ isValid: isValid, value: filteredValue }),
  82. err => ({ isValid: err })
  83. ),
  84. err => ({ isValid: err })
  85. )
  86. )
  87. .share();
  88. var success = validation.filter(state => state.isValid === true).take(1);
  89. var error = validation.filter(state => state.isValid !== true).takeUntil(success);
  90. return {
  91. success: success,
  92. error: error
  93. };
  94. }
  95. /**
  96. * Generate the prompt question string
  97. * @return {String} prompt question string
  98. */
  99. getQuestion() {
  100. var message =
  101. this.opt.prefix +
  102. ' ' +
  103. chalk.bold(this.opt.message) +
  104. this.opt.suffix +
  105. chalk.reset(' ');
  106. // Append the default if available, and if question isn't answered
  107. if (this.opt.default != null && this.status !== 'answered') {
  108. message += chalk.dim('(' + this.opt.default + ') ');
  109. }
  110. return message;
  111. }
  112. }
  113. module.exports = Prompt;