index.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. 'use strict';
  2. const assert = require('assert');
  3. const Events = require('events');
  4. const utils = require('./lib/utils');
  5. /**
  6. * Create an instance of `Enquirer`.
  7. *
  8. * ```js
  9. * const Enquirer = require('enquirer');
  10. * const enquirer = new Enquirer();
  11. * ```
  12. * @name Enquirer
  13. * @param {Object} `options` (optional) Options to use with all prompts.
  14. * @param {Object} `answers` (optional) Answers object to initialize with.
  15. * @api public
  16. */
  17. class Enquirer extends Events {
  18. constructor(options, answers) {
  19. super();
  20. this.options = utils.merge({}, options);
  21. this.answers = { ...answers };
  22. }
  23. /**
  24. * Register a custom prompt type.
  25. *
  26. * ```js
  27. * const Enquirer = require('enquirer');
  28. * const enquirer = new Enquirer();
  29. * enquirer.register('customType', require('./custom-prompt'));
  30. * ```
  31. * @name register()
  32. * @param {String} `type`
  33. * @param {Function|Prompt} `fn` `Prompt` class, or a function that returns a `Prompt` class.
  34. * @return {Object} Returns the Enquirer instance
  35. * @api public
  36. */
  37. register(type, fn) {
  38. if (utils.isObject(type)) {
  39. for (let key of Object.keys(type)) this.register(key, type[key]);
  40. return this;
  41. }
  42. assert.equal(typeof fn, 'function', 'expected a function');
  43. const name = type.toLowerCase();
  44. if (fn.prototype instanceof this.Prompt) {
  45. this.prompts[name] = fn;
  46. } else {
  47. this.prompts[name] = fn(this.Prompt, this);
  48. }
  49. return this;
  50. }
  51. /**
  52. * Prompt function that takes a "question" object or array of question objects,
  53. * and returns an object with responses from the user.
  54. *
  55. * ```js
  56. * const Enquirer = require('enquirer');
  57. * const enquirer = new Enquirer();
  58. *
  59. * const response = await enquirer.prompt({
  60. * type: 'input',
  61. * name: 'username',
  62. * message: 'What is your username?'
  63. * });
  64. * console.log(response);
  65. * ```
  66. * @name prompt()
  67. * @param {Array|Object} `questions` Options objects for one or more prompts to run.
  68. * @return {Promise} Promise that returns an "answers" object with the user's responses.
  69. * @api public
  70. */
  71. async prompt(questions = []) {
  72. for (let question of [].concat(questions)) {
  73. try {
  74. if (typeof question === 'function') question = await question.call(this);
  75. await this.ask(utils.merge({}, this.options, question));
  76. } catch (err) {
  77. return Promise.reject(err);
  78. }
  79. }
  80. return this.answers;
  81. }
  82. async ask(question) {
  83. if (typeof question === 'function') {
  84. question = await question.call(this);
  85. }
  86. let opts = utils.merge({}, this.options, question);
  87. let { type, name } = question;
  88. let { set, get } = utils;
  89. if (typeof type === 'function') {
  90. type = await type.call(this, question, this.answers);
  91. }
  92. if (!type) return this.answers[name];
  93. if (type === 'number') type = 'numeral';
  94. assert(this.prompts[type], `Prompt "${type}" is not registered`);
  95. let prompt = new this.prompts[type](opts);
  96. let value = get(this.answers, name);
  97. prompt.state.answers = this.answers;
  98. prompt.enquirer = this;
  99. if (name) {
  100. prompt.on('submit', value => {
  101. this.emit('answer', name, value, prompt);
  102. set(this.answers, name, value);
  103. });
  104. }
  105. // bubble events
  106. let emit = prompt.emit.bind(prompt);
  107. prompt.emit = (...args) => {
  108. this.emit.call(this, ...args);
  109. return emit(...args);
  110. };
  111. this.emit('prompt', prompt, this);
  112. if (opts.autofill && value != null) {
  113. prompt.value = prompt.input = value;
  114. // if "autofill=show" render the prompt, otherwise stay "silent"
  115. if (opts.autofill === 'show') {
  116. await prompt.submit();
  117. }
  118. } else {
  119. value = prompt.value = await prompt.run();
  120. }
  121. return value;
  122. }
  123. /**
  124. * Use an enquirer plugin.
  125. *
  126. * ```js
  127. * const Enquirer = require('enquirer');
  128. * const enquirer = new Enquirer();
  129. * const plugin = enquirer => {
  130. * // do stuff to enquire instance
  131. * };
  132. * enquirer.use(plugin);
  133. * ```
  134. * @name use()
  135. * @param {Function} `plugin` Plugin function that takes an instance of Enquirer.
  136. * @return {Object} Returns the Enquirer instance.
  137. * @api public
  138. */
  139. use(plugin) {
  140. plugin.call(this, this);
  141. return this;
  142. }
  143. set Prompt(value) {
  144. this._Prompt = value;
  145. }
  146. get Prompt() {
  147. return this._Prompt || this.constructor.Prompt;
  148. }
  149. get prompts() {
  150. return this.constructor.prompts;
  151. }
  152. static set Prompt(value) {
  153. this._Prompt = value;
  154. }
  155. static get Prompt() {
  156. return this._Prompt || require('./lib/prompt');
  157. }
  158. static get prompts() {
  159. return require('./lib/prompts');
  160. }
  161. static get types() {
  162. return require('./lib/types');
  163. }
  164. /**
  165. * Prompt function that takes a "question" object or array of question objects,
  166. * and returns an object with responses from the user.
  167. *
  168. * ```js
  169. * const { prompt } = require('enquirer');
  170. * const response = await prompt({
  171. * type: 'input',
  172. * name: 'username',
  173. * message: 'What is your username?'
  174. * });
  175. * console.log(response);
  176. * ```
  177. * @name Enquirer#prompt
  178. * @param {Array|Object} `questions` Options objects for one or more prompts to run.
  179. * @return {Promise} Promise that returns an "answers" object with the user's responses.
  180. * @api public
  181. */
  182. static get prompt() {
  183. const fn = (questions, ...rest) => {
  184. let enquirer = new this(...rest);
  185. let emit = enquirer.emit.bind(enquirer);
  186. enquirer.emit = (...args) => {
  187. fn.emit(...args);
  188. return emit(...args);
  189. };
  190. return enquirer.prompt(questions);
  191. };
  192. utils.mixinEmitter(fn, new Events());
  193. return fn;
  194. }
  195. }
  196. utils.mixinEmitter(Enquirer, new Events());
  197. const prompts = Enquirer.prompts;
  198. for (let name of Object.keys(prompts)) {
  199. let key = name.toLowerCase();
  200. let run = options => new prompts[name](options).run();
  201. Enquirer.prompt[key] = run;
  202. Enquirer[key] = run;
  203. if (!Enquirer[name]) {
  204. Reflect.defineProperty(Enquirer, name, { get: () => prompts[name] });
  205. }
  206. }
  207. const define = name => {
  208. utils.defineExport(Enquirer, name, () => Enquirer.types[name]);
  209. };
  210. define('ArrayPrompt');
  211. define('AuthPrompt');
  212. define('BooleanPrompt');
  213. define('NumberPrompt');
  214. define('StringPrompt');
  215. module.exports = Enquirer;