source: trip-planner-front/node_modules/inquirer/lib/ui/prompt.js@ 8d391a1

Last change on this file since 8d391a1 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

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