prompt.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. 'use strict';
  2. var _ = {
  3. isPlainObject: require('lodash/isPlainObject'),
  4. clone: require('lodash/clone'),
  5. isArray: require('lodash/isArray'),
  6. set: require('lodash/set'),
  7. isFunction: require('lodash/isFunction'),
  8. };
  9. var { defer, empty, from, of } = require('rxjs');
  10. var { concatMap, filter, publish, reduce } = require('rxjs/operators');
  11. var runAsync = require('run-async');
  12. var utils = require('../utils/utils');
  13. var Base = require('./baseUI');
  14. /**
  15. * Base interface class other can inherits from
  16. */
  17. class PromptUI extends Base {
  18. constructor(prompts, opt) {
  19. super(opt);
  20. this.prompts = prompts;
  21. }
  22. run(questions, answers) {
  23. // Keep global reference to the answers
  24. if (_.isPlainObject(answers)) {
  25. this.answers = _.clone(answers);
  26. } else {
  27. this.answers = {};
  28. }
  29. // Make sure questions is an array.
  30. if (_.isPlainObject(questions)) {
  31. questions = [questions];
  32. }
  33. // Create an observable, unless we received one as parameter.
  34. // Note: As this is a public interface, we cannot do an instanceof check as we won't
  35. // be using the exact same object in memory.
  36. var obs = _.isArray(questions) ? from(questions) : questions;
  37. this.process = obs.pipe(
  38. concatMap(this.processQuestion.bind(this)),
  39. publish() // Creates a hot Observable. It prevents duplicating prompts.
  40. );
  41. this.process.connect();
  42. return this.process
  43. .pipe(
  44. reduce((answers, answer) => {
  45. _.set(answers, answer.name, answer.answer);
  46. return answers;
  47. }, this.answers)
  48. )
  49. .toPromise(Promise)
  50. .then(this.onCompletion.bind(this), this.onError.bind(this));
  51. }
  52. /**
  53. * Once all prompt are over
  54. */
  55. onCompletion() {
  56. this.close();
  57. return this.answers;
  58. }
  59. onError(error) {
  60. this.close();
  61. return Promise.reject(error);
  62. }
  63. processQuestion(question) {
  64. question = _.clone(question);
  65. return defer(() => {
  66. var obs = of(question);
  67. return obs.pipe(
  68. concatMap(this.setDefaultType.bind(this)),
  69. concatMap(this.filterIfRunnable.bind(this)),
  70. concatMap(() =>
  71. utils.fetchAsyncQuestionProperty(question, 'message', this.answers)
  72. ),
  73. concatMap(() =>
  74. utils.fetchAsyncQuestionProperty(question, 'default', this.answers)
  75. ),
  76. concatMap(() =>
  77. utils.fetchAsyncQuestionProperty(question, 'choices', this.answers)
  78. ),
  79. concatMap(this.fetchAnswer.bind(this))
  80. );
  81. });
  82. }
  83. fetchAnswer(question) {
  84. var Prompt = this.prompts[question.type];
  85. this.activePrompt = new Prompt(question, this.rl, this.answers);
  86. return defer(() =>
  87. from(
  88. this.activePrompt
  89. .run()
  90. .then((answer) => ({ name: question.name, answer: answer }))
  91. )
  92. );
  93. }
  94. setDefaultType(question) {
  95. // Default type to input
  96. if (!this.prompts[question.type]) {
  97. question.type = 'input';
  98. }
  99. return defer(() => of(question));
  100. }
  101. filterIfRunnable(question) {
  102. if (question.askAnswered !== true && this.answers[question.name] !== undefined) {
  103. return empty();
  104. }
  105. if (question.when === false) {
  106. return empty();
  107. }
  108. if (!_.isFunction(question.when)) {
  109. return of(question);
  110. }
  111. var answers = this.answers;
  112. return defer(() =>
  113. from(
  114. runAsync(question.when)(answers).then((shouldRun) => {
  115. if (shouldRun) {
  116. return question;
  117. }
  118. })
  119. ).pipe(filter((val) => val != null))
  120. );
  121. }
  122. }
  123. module.exports = PromptUI;